| @@ -55,7 +55,7 @@ namespace cppcore | |||||
| || *i == '\0')) | || *i == '\0')) | ||||
| { | { | ||||
| std::string tmp(s, convert_cast<size_t>(i - s)); | std::string tmp(s, convert_cast<size_t>(i - s)); | ||||
| if (!predicate(tmp)) | |||||
| if (!predicate(std::move(tmp))) | |||||
| return false; | return false; | ||||
| s = i + 1; | s = i + 1; | ||||
| } | } | ||||
| @@ -436,7 +436,7 @@ namespace cppcore | |||||
| inline bool operator()(const std::string& str, std::list<T>& value) const | inline bool operator()(const std::string& str, std::list<T>& value) const | ||||
| { | { | ||||
| std::list<T> tmp; | std::list<T> tmp; | ||||
| auto ret = string_split(str, ',', [&tmp](auto& s) { | |||||
| auto ret = string_split(str, ',', [&tmp](auto&& s) { | |||||
| tmp.emplace_back(); | tmp.emplace_back(); | ||||
| return try_from_string(s, tmp.back()); | return try_from_string(s, tmp.back()); | ||||
| }); | }); | ||||
| @@ -11,27 +11,137 @@ | |||||
| namespace cppcore | namespace cppcore | ||||
| { | { | ||||
| /** | |||||
| * @brief Basic exception with stack trace and some more small features. | |||||
| */ | |||||
| struct exception | |||||
| : public std::exception | |||||
| template<size_t N> | |||||
| struct stack_trace | |||||
| { | { | ||||
| public: | public: | ||||
| enum class print_flag | enum class print_flag | ||||
| { | { | ||||
| print_trace, | |||||
| resolve_address, | |||||
| print_trace, //!< print the stack trace | |||||
| print_number, //!< add a number before the stack entry | |||||
| resolve_symbol, //!< resolve the symbol | |||||
| resolve_filename, //!< resolve the filename | |||||
| }; | }; | ||||
| using print_flags = shifted_flags<print_flag>; | using print_flags = shifted_flags<print_flag>; | ||||
| private: | |||||
| using symbols = std::unique_ptr<char*, decltype(&free)>; | |||||
| private: | |||||
| void* _stack[N]; //!< Stack addresses. | |||||
| size_t _stack_size; //!< Number of elements actually stored in _stack. | |||||
| mutable symbols _symbols; //!< List of resolved symbols. | |||||
| public: | |||||
| static const size_t max_stack_size = N; | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| inline stack_trace(); | |||||
| /** | |||||
| * @brief Move constructor. | |||||
| */ | |||||
| inline stack_trace(stack_trace&& other); | |||||
| /** | |||||
| * @brief Copy constructor. | |||||
| */ | |||||
| inline stack_trace(const stack_trace& other); | |||||
| /** | |||||
| * @brief Move assignment constructor. | |||||
| */ | |||||
| inline stack_trace& operator=(stack_trace&& other); | |||||
| /** | |||||
| * @brief Copy assigment constructor. | |||||
| */ | |||||
| inline stack_trace& operator=(const stack_trace& other); | |||||
| /** | |||||
| * @brief Get the number of stack entries. | |||||
| */ | |||||
| inline size_t size() const; | |||||
| /** | |||||
| * @brief Get the stack entry at the given index. | |||||
| */ | |||||
| inline void* address(size_t index) const; | |||||
| /** | |||||
| * @brief Get the symbol at the given index. | |||||
| */ | |||||
| inline const char* symbol(size_t index) const; | |||||
| /** | |||||
| * @brief Get the filename at the given index. | |||||
| */ | |||||
| inline std::string filename(size_t index) const; | |||||
| /** | |||||
| * @brief Print the whole stack trace to the passed stream. | |||||
| * | |||||
| * @param[in] os Stream to print exception to. | |||||
| * @param[in] flags Flags to use for printing. | |||||
| */ | |||||
| inline void print( | |||||
| std::ostream& os, | |||||
| const print_flags& flags) const; | |||||
| /** | |||||
| * @brief Convert the stack trace to string. | |||||
| * | |||||
| * @param[in] flags Flags to use for printing. | |||||
| * | |||||
| * @return String representation of the exception. | |||||
| */ | |||||
| inline std::string print( | |||||
| const print_flags& flags) const; | |||||
| /** | |||||
| * @brief Print the whole stack trace to the passed stream. | |||||
| * | |||||
| * @param[in] os Stream to print exception to. | |||||
| * @param[in] index Index of trace element to print. | |||||
| * @param[in] flags Flags to use for printing. | |||||
| */ | |||||
| inline void print( | |||||
| std::ostream& os, | |||||
| size_t index, | |||||
| const print_flags& flags) const; | |||||
| /** | |||||
| * @brief Convert the stack trace to string. | |||||
| * | |||||
| * @param[in] index Index of trace element to print. | |||||
| * @param[in] flags Flags to use for printing. | |||||
| * | |||||
| * @return String representation of the exception. | |||||
| */ | |||||
| inline std::string print( | |||||
| size_t index, | |||||
| const print_flags& flags) const; | |||||
| }; | |||||
| /** | |||||
| * @brief Basic exception with stack trace and some more small features. | |||||
| */ | |||||
| struct exception | |||||
| : public std::exception | |||||
| { | |||||
| public: | public: | ||||
| static constexpr size_t max_stack_size = 15; | static constexpr size_t max_stack_size = 15; | ||||
| using stack_trace = cppcore::stack_trace<max_stack_size>; | |||||
| using print_flag = typename stack_trace::print_flag; | |||||
| using print_flags = typename stack_trace::print_flags; | |||||
| /** | /** | ||||
| * @brief Get the default print flags. | |||||
| * @brief Get the default exception print flags. | |||||
| */ | */ | ||||
| static inline const print_flags& default_print_flag(); | |||||
| static inline const print_flags& default_print_flags(); | |||||
| /** | /** | ||||
| * @brief Print the exception to the passed stream; | * @brief Print the exception to the passed stream; | ||||
| @@ -48,9 +158,8 @@ namespace cppcore | |||||
| mutable std::string _msg_cache; //!< Caches the message of the exception. | mutable std::string _msg_cache; //!< Caches the message of the exception. | ||||
| public: | public: | ||||
| std::string message; //!< message of the exception | |||||
| void* stack[max_stack_size]; //!< stack trace | |||||
| int stack_size; //!< number of entries stored in stack | |||||
| std::string message; //!< message of the exception | |||||
| stack_trace trace; //!< stack trace | |||||
| public: | public: | ||||
| /** | /** | ||||
| @@ -76,7 +185,7 @@ namespace cppcore | |||||
| */ | */ | ||||
| inline void print( | inline void print( | ||||
| std::ostream& os, | std::ostream& os, | ||||
| const print_flags& flags = default_print_flag()) const; | |||||
| const stack_trace::print_flags& flags = default_print_flags()) const; | |||||
| /** | /** | ||||
| * @brief Convert the exception to string. | * @brief Convert the exception to string. | ||||
| @@ -86,7 +195,7 @@ namespace cppcore | |||||
| * @return String representation of the exception. | * @return String representation of the exception. | ||||
| */ | */ | ||||
| inline std::string print( | inline std::string print( | ||||
| const print_flags& flags = default_print_flag()) const; | |||||
| const stack_trace::print_flags& flags = default_print_flags()) const; | |||||
| /** | /** | ||||
| * @brief Convert the exception to a string. | * @brief Convert the exception to a string. | ||||
| @@ -1,17 +1,185 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <memory> | |||||
| #include "exception.h" | #include "exception.h" | ||||
| namespace cppcore | namespace cppcore | ||||
| { | { | ||||
| /* stack_trace */ | |||||
| template<size_t N> | |||||
| stack_trace<N>::stack_trace() | |||||
| : _stack_size (static_cast<size_t>(backtrace(&_stack[0], max_stack_size))) | |||||
| , _symbols (nullptr, &free) | |||||
| { } | |||||
| template<size_t N> | |||||
| stack_trace<N>::stack_trace(stack_trace&& other) | |||||
| : _stack_size (std::move(other)._stack_size) | |||||
| , _symbols (std::move(other)._symbols) | |||||
| { memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); } | |||||
| template<size_t N> | |||||
| stack_trace<N>::stack_trace(const stack_trace& other) | |||||
| : _stack_size (std::move(other)._stack_size) | |||||
| , _symbols (nullptr, &free) | |||||
| { memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); } | |||||
| template<size_t N> | |||||
| stack_trace<N>& stack_trace<N>::operator=(stack_trace&& other) | |||||
| { | |||||
| _symbols = std::move(other)._symbols; | |||||
| _stack_size = std::move(other)._stack_size; | |||||
| memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); | |||||
| } | |||||
| template<size_t N> | |||||
| stack_trace<N>& stack_trace<N>::operator=(const stack_trace& other) | |||||
| { | |||||
| _symbols.reset(); | |||||
| _stack_size = other._stack_size; | |||||
| memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); | |||||
| } | |||||
| template<size_t N> | |||||
| size_t stack_trace<N>::size() const | |||||
| { return _stack_size; } | |||||
| template<size_t N> | |||||
| void* stack_trace<N>::address(size_t index) const | |||||
| { | |||||
| return index >= _stack_size | |||||
| ? nullptr | |||||
| : _stack[index]; | |||||
| } | |||||
| template<size_t N> | |||||
| const char* stack_trace<N>::symbol(size_t index) const | |||||
| { | |||||
| if (!_symbols) | |||||
| _symbols.reset(backtrace_symbols(_stack, static_cast<int>(_stack_size))); | |||||
| return index >= _stack_size | |||||
| ? nullptr | |||||
| : _symbols.get()[index]; | |||||
| } | |||||
| template<size_t N> | |||||
| std::string stack_trace<N>::filename(size_t index) const | |||||
| { | |||||
| #if defined(__linux__) | |||||
| /* get the symbol */ | |||||
| auto line = symbol(index); | |||||
| if (!line) | |||||
| return std::string(); | |||||
| /* find the filename of the symbol */ | |||||
| int p = 0; | |||||
| while ( line[p] != '(' | |||||
| && line[p] != ' ' | |||||
| && line[p] != 0) | |||||
| ++p; | |||||
| /* build the command */ | |||||
| char cmd[256]; | |||||
| sprintf(cmd, "addr2line %p -e %.*s", _stack[index], p, line); | |||||
| /* execute the command */ | |||||
| std::array<char, 128> buffer; | |||||
| std::string result; | |||||
| std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose); | |||||
| if (!pipe) | |||||
| return std::string(); | |||||
| /* collect the output */ | |||||
| while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) | |||||
| result += buffer.data(); | |||||
| /* remove trailing whitespaces */ | |||||
| while ( !result.empty() | |||||
| && ( result.back() == '\n' | |||||
| || result.back() == '\r' | |||||
| || result.back() == '\t' | |||||
| || result.back() == ' ')) | |||||
| result.pop_back(); | |||||
| return result; | |||||
| #else | |||||
| return std::string(); | |||||
| #endif | |||||
| } | |||||
| template<size_t N> | |||||
| void stack_trace<N>::print(std::ostream& os, const print_flags& flags) const | |||||
| { | |||||
| for (size_t i = 0; i < size(); ++i) | |||||
| print(os, i, flags); | |||||
| } | |||||
| template<size_t N> | |||||
| std::string stack_trace<N>::print(const print_flags& flags) const | |||||
| { | |||||
| std::ostringstream os; | |||||
| print(os, flags); | |||||
| return os.str(); | |||||
| } | |||||
| template<size_t N> | |||||
| void stack_trace<N>::print( | |||||
| std::ostream& os, | |||||
| size_t index, | |||||
| const print_flags& flags) const | |||||
| { | |||||
| os << " "; | |||||
| if (flags.is_set(print_flag::print_number)) | |||||
| { | |||||
| os << "#" | |||||
| << std::setw(2) | |||||
| << std::setfill('0') | |||||
| << index | |||||
| << ' '; | |||||
| } | |||||
| os << "[0x" << std::setw(2 * sizeof(void*)) | |||||
| << std::setfill('0') | |||||
| << std::hex | |||||
| << reinterpret_cast<uintptr_t>(address(index)) << "]"; | |||||
| if (flags.is_set(print_flag::resolve_symbol)) | |||||
| { | |||||
| os << " " | |||||
| << symbol(index); | |||||
| } | |||||
| if (flags.is_set(print_flag::resolve_filename)) | |||||
| { | |||||
| auto str = filename(index); | |||||
| if (!str.empty()) | |||||
| { | |||||
| if (flags.is_set(print_flag::resolve_symbol)) | |||||
| os << std::endl << " "; | |||||
| os << " " << filename(index); | |||||
| } | |||||
| } | |||||
| os << std::endl; | |||||
| } | |||||
| template<size_t N> | |||||
| std::string stack_trace<N>::print( | |||||
| size_t index, | |||||
| const print_flags& flags) const | |||||
| { | |||||
| std::ostringstream os; | |||||
| print(os, index, flags); | |||||
| return os.str(); | |||||
| } | |||||
| /* exception */ | /* exception */ | ||||
| const exception::print_flags& exception::default_print_flag() | |||||
| const exception::print_flags& exception::default_print_flags() | |||||
| { | { | ||||
| static const print_flags value({ | static const print_flags value({ | ||||
| print_flag::print_trace, | print_flag::print_trace, | ||||
| print_flag::resolve_address, | |||||
| print_flag::print_number, | |||||
| print_flag::resolve_symbol, | |||||
| }); | }); | ||||
| return value; | return value; | ||||
| } | } | ||||
| @@ -22,31 +190,23 @@ namespace cppcore | |||||
| exception::exception(std::string msg) | exception::exception(std::string msg) | ||||
| : message (msg) | : message (msg) | ||||
| , stack_size (0) | |||||
| , _msg_cache_empty (true) | , _msg_cache_empty (true) | ||||
| { | |||||
| stack_size = backtrace(&stack[0], max_stack_size); | |||||
| } | |||||
| { } | |||||
| void exception::print(std::ostream& os, const print_flags& flags) const | |||||
| void exception::print( | |||||
| std::ostream& os, | |||||
| const stack_trace::print_flags& flags) const | |||||
| { | { | ||||
| print_message(os); | print_message(os); | ||||
| if (!flags.is_set(print_flag::print_trace)) | |||||
| return; | |||||
| os << std::endl; | |||||
| char** lines = nullptr; | |||||
| if (flags.is_set(print_flag::resolve_address)) | |||||
| lines = backtrace_symbols(stack, stack_size); | |||||
| for (int i = 0; i < stack_size; ++i) | |||||
| if (flags.is_set(print_flag::print_trace)) | |||||
| { | { | ||||
| os << " [0x" << std::setw(2 * sizeof(void*)) << std::setfill('0') << std::hex << reinterpret_cast<uintptr_t>(stack[i]) << "]"; | |||||
| if (lines && lines[i]) | |||||
| os << " " << lines[i]; | |||||
| os << std::endl; | os << std::endl; | ||||
| trace.print(os, flags); | |||||
| } | } | ||||
| } | } | ||||
| std::string exception::print(const print_flags& flags) const | |||||
| std::string exception::print( | |||||
| const stack_trace::print_flags& flags) const | |||||
| { | { | ||||
| std::ostringstream os; | std::ostringstream os; | ||||
| print(os, flags); | print(os, flags); | ||||