@@ -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); | ||||