2019-10-06 16:45:29 +02:00
|
|
|
// Copyright Johannes Kapfhammer 2019.
|
|
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
|
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
|
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
|
|
|
// A debug macro: allows debugging with dbg(<expression>);
|
|
|
|
// initialize with dbg_init() to enable colorized output
|
|
|
|
//
|
|
|
|
|
2019-10-06 17:16:19 +02:00
|
|
|
#ifndef SOI_DBG
|
|
|
|
#define SOI_DBG
|
2019-10-06 16:45:29 +02:00
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <cstring>
|
|
|
|
#include <ostream>
|
2019-10-06 17:19:10 +02:00
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
2019-10-06 16:45:29 +02:00
|
|
|
|
|
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "soi-pretty.hpp"
|
|
|
|
|
|
|
|
namespace soi_h {
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// detecting the type name: https://stackoverflow.com/a/20170989
|
|
|
|
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
# if __cplusplus < 201103
|
|
|
|
# define CONSTEXPR11_TN
|
|
|
|
# define CONSTEXPR14_TN
|
|
|
|
# define NOEXCEPT_TN
|
|
|
|
# elif __cplusplus < 201402
|
|
|
|
# define CONSTEXPR11_TN constexpr
|
|
|
|
# define CONSTEXPR14_TN
|
|
|
|
# define NOEXCEPT_TN noexcept
|
|
|
|
# else
|
|
|
|
# define CONSTEXPR11_TN constexpr
|
|
|
|
# define CONSTEXPR14_TN constexpr
|
|
|
|
# define NOEXCEPT_TN noexcept
|
|
|
|
# endif
|
|
|
|
#else // _MSC_VER
|
|
|
|
# if _MSC_VER < 1900
|
|
|
|
# define CONSTEXPR11_TN
|
|
|
|
# define CONSTEXPR14_TN
|
|
|
|
# define NOEXCEPT_TN
|
|
|
|
# elif _MSC_VER < 2000
|
|
|
|
# define CONSTEXPR11_TN constexpr
|
|
|
|
# define CONSTEXPR14_TN
|
|
|
|
# define NOEXCEPT_TN noexcept
|
|
|
|
# else
|
|
|
|
# define CONSTEXPR11_TN constexpr
|
|
|
|
# define CONSTEXPR14_TN constexpr
|
|
|
|
# define NOEXCEPT_TN noexcept
|
|
|
|
# endif
|
|
|
|
#endif // _MSC_VER
|
|
|
|
|
|
|
|
class static_string {
|
|
|
|
const char* const p_;
|
|
|
|
const std::size_t sz_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
typedef const char* const_iterator;
|
|
|
|
|
|
|
|
template <std::size_t N>
|
|
|
|
CONSTEXPR11_TN static_string(const char(&a)[N]) NOEXCEPT_TN
|
|
|
|
: p_(a)
|
|
|
|
, sz_(N-1)
|
|
|
|
{}
|
|
|
|
|
|
|
|
CONSTEXPR11_TN static_string(const char* p, std::size_t N) NOEXCEPT_TN
|
|
|
|
: p_(p)
|
|
|
|
, sz_(N)
|
|
|
|
{}
|
|
|
|
|
|
|
|
CONSTEXPR11_TN const char* data() const NOEXCEPT_TN {return p_;}
|
|
|
|
CONSTEXPR11_TN std::size_t size() const NOEXCEPT_TN {return sz_;}
|
|
|
|
|
|
|
|
CONSTEXPR11_TN const_iterator begin() const NOEXCEPT_TN {return p_;}
|
|
|
|
CONSTEXPR11_TN const_iterator end() const NOEXCEPT_TN {return p_ + sz_;}
|
|
|
|
|
|
|
|
CONSTEXPR11_TN char operator[](std::size_t n) const
|
|
|
|
{
|
|
|
|
return n < sz_ ? p_[n] : throw std::out_of_range("static_string");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct static_string_no_std {
|
|
|
|
bool is_debug;
|
|
|
|
bool is_std;
|
|
|
|
static_string type;
|
|
|
|
CONSTEXPR11_TN static_string_no_std(bool is_debug,
|
|
|
|
bool is_std,
|
|
|
|
static_string type)
|
|
|
|
: is_debug(is_debug), is_std(is_std), type(type) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
inline
|
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& os, static_string const& s)
|
|
|
|
{
|
|
|
|
return os.write(s.data(), s.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
inline
|
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& os, static_string_no_std const& s) {
|
|
|
|
if (s.is_debug) {
|
|
|
|
const int k = sizeof("std::__debug::") - 1;
|
|
|
|
return os.write(s.type.data()+k, s.type.size()-k);
|
|
|
|
} else if (s.is_std) {
|
|
|
|
const int k = sizeof("std::") - 1;
|
|
|
|
return os.write(s.type.data()+k, s.type.size()-k);
|
|
|
|
} else {
|
|
|
|
return os.write(s.type.data(), s.type.size());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
CONSTEXPR14_TN
|
|
|
|
static_string
|
|
|
|
type_name()
|
|
|
|
{
|
|
|
|
const int k = sizeof("constexpr soi_h::detail:: T =")/sizeof(char);
|
|
|
|
#ifdef __clang__
|
|
|
|
static_string p = __PRETTY_FUNCTION__;
|
|
|
|
return static_string(p.data() + 31 + k, p.size() - 31 - k - 1);
|
|
|
|
#elif defined(__GNUC__)
|
|
|
|
static_string p = __PRETTY_FUNCTION__;
|
|
|
|
# if __cplusplus < 201402
|
|
|
|
return static_string(p.data() + 36 + k, p.size() - 36 - k - 1);
|
|
|
|
# else
|
|
|
|
return static_string(p.data() + 46 + k, p.size() - 46 - k - 1);
|
|
|
|
# endif
|
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
static_string p = __FUNCSIG__;
|
|
|
|
return static_string(p.data() + 38 + k, p.size() - 38 - k - 7);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
CONSTEXPR14_TN
|
|
|
|
static_string_no_std
|
|
|
|
sanitized_type_name() {
|
|
|
|
CONSTEXPR14_TN static_string t = type_name<T>();
|
|
|
|
CONSTEXPR14_TN bool is_std =
|
|
|
|
t.size() >= sizeof("std::") &&
|
|
|
|
t[ 0] == 's' &&
|
|
|
|
t[ 1] == 't' &&
|
|
|
|
t[ 2] == 'd' &&
|
|
|
|
t[ 3] == ':' &&
|
|
|
|
t[ 4] == ':';
|
|
|
|
CONSTEXPR14_TN bool is_debug =
|
|
|
|
is_std &&
|
|
|
|
t.size() >= sizeof("std::__debug::") &&
|
|
|
|
t[ 5] == '_' &&
|
|
|
|
t[ 6] == '_' &&
|
|
|
|
t[ 7] == 'd' &&
|
|
|
|
t[ 8] == 'e' &&
|
|
|
|
t[ 9] == 'b' &&
|
|
|
|
t[10] == 'u' &&
|
|
|
|
t[11] == 'g';
|
|
|
|
return static_string_no_std(is_debug, is_std, t);
|
|
|
|
}
|
2019-10-06 17:16:19 +02:00
|
|
|
|
2019-10-06 16:45:29 +02:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// colorized output
|
|
|
|
|
|
|
|
bool tty_supports_colors() {
|
|
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
|
|
|
return isatty(STDERR_FILENO);
|
|
|
|
#else
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int has_environment_color_overwrite() {
|
2019-10-06 17:16:19 +02:00
|
|
|
if (const char* color_enabled = std::getenv("SOI_COLOR")) {
|
2019-10-06 16:45:29 +02:00
|
|
|
if (!std::strcmp(color_enabled, "1"))
|
|
|
|
return 1;
|
|
|
|
if (!std::strcmp(color_enabled, "0"))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool are_colors_enabled() {
|
|
|
|
int c = has_environment_color_overwrite();
|
|
|
|
if (c == -1)
|
|
|
|
return tty_supports_colors();
|
|
|
|
return static_cast<bool>(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// init and static variables
|
|
|
|
|
|
|
|
static bool colors_enabled = false;
|
|
|
|
static const char* ANSI_DEBUG = "";
|
|
|
|
static const char* ANSI_EXPRESSION = "";
|
|
|
|
static const char* ANSI_VALUE = "";
|
|
|
|
static const char* ANSI_TYPE = "";
|
|
|
|
static const char* ANSI_MESSAGE = "";
|
|
|
|
static const char* ANSI_RESET = "";
|
|
|
|
|
|
|
|
void dbg_init(bool with_colors) {
|
|
|
|
if (with_colors) {
|
|
|
|
bool colors_enabled = true;
|
|
|
|
ANSI_DEBUG = "\x1b[02m";
|
|
|
|
ANSI_EXPRESSION = "\x1b[36m";
|
|
|
|
ANSI_VALUE = "\x1b[01m";
|
|
|
|
ANSI_TYPE = "\x1b[32m";
|
|
|
|
ANSI_MESSAGE = "\x1b[31m\x1b[01m";
|
|
|
|
ANSI_RESET = "\x1b[0m";
|
|
|
|
} else {
|
|
|
|
bool colors_enabled = false;
|
|
|
|
ANSI_DEBUG = "";
|
|
|
|
ANSI_EXPRESSION = "";
|
|
|
|
ANSI_VALUE = "";
|
|
|
|
ANSI_TYPE = "";
|
|
|
|
ANSI_MESSAGE = "";
|
|
|
|
ANSI_RESET = "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void dbg_init() {
|
|
|
|
dbg_init(are_colors_enabled());
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// printer
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T&& dbg_print(T&& value,
|
|
|
|
static_string_no_std const& type,
|
|
|
|
char const* file,
|
|
|
|
int line,
|
|
|
|
char const* function_name,
|
|
|
|
char const* expression) {
|
|
|
|
const T& ref = value;
|
|
|
|
std::stringstream value_buffer; // avoid nesting of dbg macros within print functinos
|
|
|
|
soi_h::pretty_print(value_buffer, ref);
|
|
|
|
|
|
|
|
std::cerr << ANSI_DEBUG
|
|
|
|
<< "[" << file << ":" << line
|
|
|
|
<< " (" << function_name << ")] " << ANSI_RESET
|
|
|
|
<< ANSI_EXPRESSION << expression << ANSI_RESET
|
|
|
|
<< " = "
|
|
|
|
<< ANSI_VALUE << value_buffer.rdbuf() << ANSI_RESET
|
|
|
|
<< " (" << ANSI_TYPE << type << ANSI_RESET << ")"
|
|
|
|
<< '\n';
|
|
|
|
|
|
|
|
return std::forward<T>(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<unsigned int N>
|
|
|
|
auto dbg_print(const char (&msg)[N],
|
|
|
|
static_string_no_std const&,
|
|
|
|
char const* file,
|
|
|
|
int line,
|
|
|
|
char const* function_name,
|
2019-10-06 17:16:19 +02:00
|
|
|
char const* expression) -> decltype(msg) {
|
2019-10-06 16:45:29 +02:00
|
|
|
std::cerr << ANSI_DEBUG
|
|
|
|
<< "[" << file << ":" << line
|
|
|
|
<< " (" << function_name << ")] " << ANSI_RESET
|
|
|
|
<< ANSI_MESSAGE << msg << ANSI_RESET
|
|
|
|
<< '\n';
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T&& identity(T&& t) {
|
|
|
|
return std::forward<T>(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end namespace detail
|
|
|
|
|
|
|
|
} // end namespace dbg_macro
|
|
|
|
|
2019-10-06 17:16:19 +02:00
|
|
|
#ifdef SOI_RELEASE
|
2019-10-06 16:45:29 +02:00
|
|
|
#define dbg(...) dbg_macro::identity(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define dbg(...) \
|
|
|
|
soi_h::detail::dbg_print((__VA_ARGS__), \
|
|
|
|
soi_h::detail::sanitized_type_name<decltype(__VA_ARGS__)>(), \
|
|
|
|
__FILE__, \
|
|
|
|
__LINE__, \
|
|
|
|
__func__, \
|
|
|
|
#__VA_ARGS__)
|
|
|
|
#endif
|
|
|
|
|
2019-10-06 17:16:19 +02:00
|
|
|
#endif // SOI_DBG
|