// 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(); // initialize with dbg_init() to enable colorized output // #ifndef SOI_DBG #define SOI_DBG #include #include #include #include #include #include #ifdef __GNUG__ #include #include #include #endif #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) #include #endif #include "soi-pretty.hpp" namespace soi { namespace detail { // ---------------------------------------------------------------------------- // pretty printing the type name struct string_view { char const* data; std::size_t size; }; template constexpr string_view get_name() { char const* p = __PRETTY_FUNCTION__; while (*p++ != '='); for (; *p == ' '; ++p); char const* p2 = p; int count = 1; for (;;++p2) { switch (*p2) { case '[': ++count; break; case ']': --count; if (!count) return {p, std::size_t(p2 - p)}; } } return {}; } std::string replace_all(std::string str, const std::string& from, const std::string& to) { size_t start_pos = 0; while((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); // Handles case where 'to' is a substring of 'from' } return str; } std::string sanitize_type(std::string s) { auto f = [&](const char *a, const char *b) { s = replace_all(std::move(s), a, b); }; f("std::", ""); f("__debug::", ""); f("__cxx11::", ""); f("basic_string", "string"); f("long int", "int"); return s; } template std::string sanitized_type_name() { auto t = get_name(); return sanitize_type(std::string(t.data, t.size)); } // ---------------------------------------------------------------------------- // 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(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 T &&dbg_print(T &&value, std::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(value); } template auto dbg_print(const char (&msg)[N], std::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; } void dbg_print_status(char const *file, int line, char const *function_name) { std::cerr << ANSI_VALUE << "[" << file << ":" << line << " (" << function_name << ")]" << ANSI_VALUE << '\n'; } template T &&identity(T &&t) { return std::forward(t); } } // end namespace detail } // namespace soi #ifdef SOI_RELEASE #define dbg(...) dbg_macro::identity(__VA_ARGS__) #else #define SOI_ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 #define SOI_IS_NONEMPTY(...) SOI_ARG16(1, ##__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) #define SOI_DBG_IMPL_0() \ soi::detail::dbg_print_status(__FILE__, __LINE__, __func__) #define SOI_DBG_IMPL_1(...) \ soi::detail::dbg_print((__VA_ARGS__), \ soi::detail::sanitized_type_name(), __FILE__, \ __LINE__, __func__, #__VA_ARGS__) #define SOI_CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) #define SOI_PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ #define SOI_DBG_IMPL(is_nonempty, ...) \ SOI_PRIMITIVE_CAT(SOI_DBG_IMPL_, is_nonempty)(__VA_ARGS__) #define dbg(...) \ SOI_DBG_IMPL(SOI_IS_NONEMPTY(__VA_ARGS__), __VA_ARGS__) #endif #endif // SOI_DBG