soi-header/include/bits/soi-dbg.hpp

236 lines
6.9 KiB
C++

// 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
//
#ifndef SOI_DBG
#define SOI_DBG
#include <cstddef>
#include <cstring>
#include <iostream>
#include <ostream>
#include <sstream>
#include <stdexcept>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#endif
#include "soi-pretty.hpp"
namespace soi {
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");
}
};
inline std::ostream &operator<<(std::ostream &os, static_string const &s) {
return os.write(s.data(), s.size());
}
template <class T> CONSTEXPR14_TN static_string type_name() {
const int k = sizeof("constexpr soi::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
}
constexpr bool is_prefix_of(char const *suffix, char const *s) {
return suffix[0] == '\0' ||
(suffix[0] == s[0] && is_prefix_of(suffix + 1, s + 1));
}
static char const *const type_string = "string";
template <class T> CONSTEXPR14_TN static_string sanitized_type_name() {
CONSTEXPR14_TN static_string t = type_name<T>();
CONSTEXPR14_TN std::size_t offset =
is_prefix_of("std::__debug::", t.data())
? sizeof("std::__debug::") - 1
: is_prefix_of("std::", t.data()) ? sizeof("std::") - 1 : 0;
return is_prefix_of("std::__cxx11::basic_string<char>", t.data())
? static_string(type_string, sizeof(type_string) - 2)
: static_string(t.data() + offset, t.size() - offset);
}
// ----------------------------------------------------------------------------
// 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() {
if (const char *color_enabled = std::getenv("SOI_COLOR")) {
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[37m";
ANSI_EXPRESSION = "\x1b[36m";
ANSI_VALUE = "\x1b[01m";
ANSI_TYPE = "\x1b[32m";
ANSI_MESSAGE = "\x1b[31;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 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::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 const &, char const *file,
int line, char const *function_name, char const *expression)
-> decltype(msg) {
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
} // namespace soi
#ifdef SOI_RELEASE
#define dbg(...) dbg_macro::identity(__VA_ARGS__)
#else
#define dbg(...) \
soi::detail::dbg_print( \
(__VA_ARGS__), \
soi::detail::sanitized_type_name<decltype(__VA_ARGS__)>(), __FILE__, \
__LINE__, __func__, #__VA_ARGS__)
#endif
#endif // SOI_DBG