// Copyright Johannes Kapfhammer 2019-2022. // 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_HEADER_DBG #define SOI_HEADER_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 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; } inline 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("basic_string", "string"); f("basic_string, allocator ", "string"); f("basic_string, allocator", "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)); } inline std::string extract_method_name(std::string const& pretty_function) { size_t colons = pretty_function.find("::"); size_t begin = pretty_function.substr(0,colons).rfind(" ") + 1; size_t end = pretty_function.rfind("(") - begin; return pretty_function.substr(begin,end); } // ---------------------------------------------------------------------------- // 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 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) { 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 { 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, std::string const& function_name, char const *expression) { const T &ref = value; std::stringstream value_buffer; // avoid nesting of dbg macros within print functions soi::prettyprint::print(value_buffer, ref); std::cerr << ANSI_DEBUG << "[" << file << ":" << line << " (" << function_name << ")] " << ANSI_RESET << ANSI_EXPRESSION << replace_all(expression, "int64_t", "int") << 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, std::string const& function_name, char const *) -> 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, std::string const& function_name) { std::cerr << ANSI_VALUE << "[" << file << ":" << line << " (" << function_name << ")]" << ANSI_RESET << '\n'; } template T &&identity(T &&t) { return std::forward(t); } } // end namespace detail } // namespace soi #ifdef SOI_RELEASE #define dbg(...) ::soi::detail::identity(__VA_ARGS__) #else #if defined(__clang__) #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) #else #define SOI_IS_NONEMPTY(...) __VA_OPT__(1) #define SOI_DBG_IMPL_ SOI_DBG_IMPL_0 #endif #define SOI_DBG_IMPL_0() \ soi::detail::dbg_print_status(__FILE__, __LINE__, soi::detail::extract_method_name(__PRETTY_FUNCTION__)) #define SOI_DBG_IMPL_1(...) \ soi::detail::dbg_print( \ (__VA_ARGS__), \ soi::detail::sanitized_type_name(), __FILE__, \ __LINE__, soi::detail::extract_method_name(__PRETTY_FUNCTION__), #__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_HEADER_DBG