forked from jiuyuan/InfiniTensor
858 lines
26 KiB
C++
858 lines
26 KiB
C++
/*****************************************************************************
|
|
|
|
dbg(...) macro
|
|
|
|
License (MIT):
|
|
|
|
Copyright (c) 2019 David Peter <mail@david-peter.de>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to
|
|
deal in the Software without restriction, including without limitation the
|
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
sell copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
*****************************************************************************/
|
|
|
|
#ifndef DBG_MACRO_DBG_H
|
|
#define DBG_MACRO_DBG_H
|
|
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
|
#define DBG_MACRO_UNIX
|
|
#elif defined(_MSC_VER)
|
|
#define DBG_MACRO_WINDOWS
|
|
#endif
|
|
|
|
// #ifndef DBG_MACRO_NO_WARNING
|
|
// #pragma message("WARNING: the 'dbg.h' header is included in your code base")
|
|
// #endif // DBG_MACRO_NO_WARNING
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <iomanip>
|
|
#include <ios>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#ifdef DBG_MACRO_UNIX
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if __cplusplus >= 201703L
|
|
#define DBG_MACRO_CXX_STANDARD 17
|
|
#elif __cplusplus >= 201402L
|
|
#define DBG_MACRO_CXX_STANDARD 14
|
|
#else
|
|
#define DBG_MACRO_CXX_STANDARD 11
|
|
#endif
|
|
|
|
#if DBG_MACRO_CXX_STANDARD >= 17
|
|
#include <optional>
|
|
#include <variant>
|
|
#endif
|
|
|
|
namespace dbg {
|
|
|
|
#ifdef DBG_MACRO_UNIX
|
|
inline bool isColorizedOutputEnabled() { return isatty(fileno(stderr)); }
|
|
#else
|
|
inline bool isColorizedOutputEnabled() { return true; }
|
|
#endif
|
|
|
|
struct time {};
|
|
|
|
namespace pretty_function {
|
|
|
|
// Compiler-agnostic version of __PRETTY_FUNCTION__ and constants to
|
|
// extract the template argument in `type_name_impl`
|
|
|
|
#if defined(__clang__)
|
|
#define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
|
static constexpr size_t PREFIX_LENGTH =
|
|
sizeof("const char *dbg::type_name_impl() [T = ") - 1;
|
|
static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1;
|
|
#elif defined(__GNUC__) && !defined(__clang__)
|
|
#define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
|
static constexpr size_t PREFIX_LENGTH =
|
|
sizeof("const char* dbg::type_name_impl() [with T = ") - 1;
|
|
static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1;
|
|
#elif defined(_MSC_VER)
|
|
#define DBG_MACRO_PRETTY_FUNCTION __FUNCSIG__
|
|
static constexpr size_t PREFIX_LENGTH =
|
|
sizeof("const char *__cdecl dbg::type_name_impl<") - 1;
|
|
static constexpr size_t SUFFIX_LENGTH = sizeof(">(void)") - 1;
|
|
#else
|
|
#error "This compiler is currently not supported by dbg_macro."
|
|
#endif
|
|
|
|
} // namespace pretty_function
|
|
|
|
// Formatting helpers
|
|
|
|
template <typename T> struct print_formatted {
|
|
static_assert(std::is_integral<T>::value,
|
|
"Only integral types are supported.");
|
|
|
|
print_formatted(T value, int numeric_base)
|
|
: inner(value), base(numeric_base) {}
|
|
|
|
operator T() const { return inner; }
|
|
|
|
const char *prefix() const {
|
|
switch (base) {
|
|
case 8:
|
|
return "0o";
|
|
case 16:
|
|
return "0x";
|
|
case 2:
|
|
return "0b";
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
T inner;
|
|
int base;
|
|
};
|
|
|
|
template <typename T> print_formatted<T> hex(T value) {
|
|
return print_formatted<T>{value, 16};
|
|
}
|
|
|
|
template <typename T> print_formatted<T> oct(T value) {
|
|
return print_formatted<T>{value, 8};
|
|
}
|
|
|
|
template <typename T> print_formatted<T> bin(T value) {
|
|
return print_formatted<T>{value, 2};
|
|
}
|
|
|
|
// Implementation of 'type_name<T>()'
|
|
|
|
template <typename T> const char *type_name_impl() {
|
|
return DBG_MACRO_PRETTY_FUNCTION;
|
|
}
|
|
|
|
template <typename T> struct type_tag {};
|
|
|
|
template <int &...ExplicitArgumentBarrier, typename T>
|
|
std::string get_type_name(type_tag<T>) {
|
|
namespace pf = pretty_function;
|
|
|
|
std::string type = type_name_impl<T>();
|
|
return type.substr(pf::PREFIX_LENGTH,
|
|
type.size() - pf::PREFIX_LENGTH - pf::SUFFIX_LENGTH);
|
|
}
|
|
|
|
template <typename T> std::string type_name() {
|
|
if (std::is_volatile<T>::value) {
|
|
if (std::is_pointer<T>::value) {
|
|
return type_name<typename std::remove_volatile<T>::type>() +
|
|
" volatile";
|
|
} else {
|
|
return "volatile " +
|
|
type_name<typename std::remove_volatile<T>::type>();
|
|
}
|
|
}
|
|
if (std::is_const<T>::value) {
|
|
if (std::is_pointer<T>::value) {
|
|
return type_name<typename std::remove_const<T>::type>() + " const";
|
|
} else {
|
|
return "const " + type_name<typename std::remove_const<T>::type>();
|
|
}
|
|
}
|
|
if (std::is_pointer<T>::value) {
|
|
return type_name<typename std::remove_pointer<T>::type>() + "*";
|
|
}
|
|
if (std::is_lvalue_reference<T>::value) {
|
|
return type_name<typename std::remove_reference<T>::type>() + "&";
|
|
}
|
|
if (std::is_rvalue_reference<T>::value) {
|
|
return type_name<typename std::remove_reference<T>::type>() + "&&";
|
|
}
|
|
return get_type_name(type_tag<T>{});
|
|
}
|
|
|
|
inline std::string get_type_name(type_tag<short>) { return "short"; }
|
|
|
|
inline std::string get_type_name(type_tag<unsigned short>) {
|
|
return "unsigned short";
|
|
}
|
|
|
|
inline std::string get_type_name(type_tag<long>) { return "long"; }
|
|
|
|
inline std::string get_type_name(type_tag<unsigned long>) {
|
|
return "unsigned long";
|
|
}
|
|
|
|
inline std::string get_type_name(type_tag<std::string>) {
|
|
return "std::string";
|
|
}
|
|
|
|
template <typename T>
|
|
std::string get_type_name(type_tag<std::vector<T, std::allocator<T>>>) {
|
|
return "std::vector<" + type_name<T>() + ">";
|
|
}
|
|
|
|
template <typename T1, typename T2>
|
|
std::string get_type_name(type_tag<std::pair<T1, T2>>) {
|
|
return "std::pair<" + type_name<T1>() + ", " + type_name<T2>() + ">";
|
|
}
|
|
|
|
template <typename... T> std::string type_list_to_string() {
|
|
std::string result;
|
|
auto unused = {(result += type_name<T>() + ", ", 0)..., 0};
|
|
static_cast<void>(unused);
|
|
|
|
#if DBG_MACRO_CXX_STANDARD >= 17
|
|
if constexpr (sizeof...(T) > 0) {
|
|
#else
|
|
if (sizeof...(T) > 0) {
|
|
#endif
|
|
result.pop_back();
|
|
result.pop_back();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename... T> std::string get_type_name(type_tag<std::tuple<T...>>) {
|
|
return "std::tuple<" + type_list_to_string<T...>() + ">";
|
|
}
|
|
|
|
template <typename T>
|
|
inline std::string get_type_name(type_tag<print_formatted<T>>) {
|
|
return type_name<T>();
|
|
}
|
|
|
|
// Implementation of 'is_detected' to specialize for container-like types
|
|
|
|
namespace detail_detector {
|
|
|
|
struct nonesuch {
|
|
nonesuch() = delete;
|
|
~nonesuch() = delete;
|
|
nonesuch(nonesuch const &) = delete;
|
|
void operator=(nonesuch const &) = delete;
|
|
};
|
|
|
|
template <typename...> using void_t = void;
|
|
|
|
template <class Default, class AlwaysVoid, template <class...> class Op,
|
|
class... Args>
|
|
struct detector {
|
|
using value_t = std::false_type;
|
|
using type = Default;
|
|
};
|
|
|
|
template <class Default, template <class...> class Op, class... Args>
|
|
struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
|
|
using value_t = std::true_type;
|
|
using type = Op<Args...>;
|
|
};
|
|
|
|
} // namespace detail_detector
|
|
|
|
template <template <class...> class Op, class... Args>
|
|
using is_detected =
|
|
typename detail_detector::detector<detail_detector::nonesuch, void, Op,
|
|
Args...>::value_t;
|
|
|
|
namespace detail {
|
|
|
|
namespace {
|
|
using std::begin;
|
|
using std::end;
|
|
#if DBG_MACRO_CXX_STANDARD < 17
|
|
template <typename T> constexpr auto size(const T &c) -> decltype(c.size()) {
|
|
return c.size();
|
|
}
|
|
template <typename T, std::size_t N>
|
|
constexpr std::size_t size(const T (&)[N]) {
|
|
return N;
|
|
}
|
|
#else
|
|
using std::size;
|
|
#endif
|
|
} // namespace
|
|
|
|
template <typename T>
|
|
using detect_begin_t = decltype(detail::begin(std::declval<T>()));
|
|
|
|
template <typename T>
|
|
using detect_end_t = decltype(detail::end(std::declval<T>()));
|
|
|
|
template <typename T>
|
|
using detect_size_t = decltype(detail::size(std::declval<T>()));
|
|
|
|
template <typename T> struct is_container {
|
|
static constexpr bool value =
|
|
is_detected<detect_begin_t, T>::value &&
|
|
is_detected<detect_end_t, T>::value &&
|
|
is_detected<detect_size_t, T>::value &&
|
|
!std::is_same<std::string,
|
|
typename std::remove_cv<typename std::remove_reference<
|
|
T>::type>::type>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
using ostream_operator_t =
|
|
decltype(std::declval<std::ostream &>() << std::declval<T>());
|
|
|
|
template <typename T>
|
|
struct has_ostream_operator : is_detected<ostream_operator_t, T> {};
|
|
|
|
} // namespace detail
|
|
|
|
// Helper to dbg(…)-print types
|
|
template <typename T> struct print_type {};
|
|
|
|
template <typename T> print_type<T> type() { return print_type<T>{}; }
|
|
|
|
// Forward declarations of "pretty_print"
|
|
|
|
template <typename T>
|
|
inline void pretty_print(std::ostream &stream, const T &value, std::true_type);
|
|
|
|
template <typename T>
|
|
inline void pretty_print(std::ostream &, const T &, std::false_type);
|
|
|
|
template <typename T>
|
|
inline typename std::enable_if<!detail::is_container<const T &>::value &&
|
|
!std::is_enum<T>::value,
|
|
bool>::type
|
|
pretty_print(std::ostream &stream, const T &value);
|
|
|
|
inline bool pretty_print(std::ostream &stream, const bool &value);
|
|
|
|
inline bool pretty_print(std::ostream &stream, const char &value);
|
|
|
|
template <typename P>
|
|
inline bool pretty_print(std::ostream &stream, P *const &value);
|
|
|
|
template <typename T, typename Deleter>
|
|
inline bool pretty_print(std::ostream &stream,
|
|
std::unique_ptr<T, Deleter> &value);
|
|
|
|
// template <typename T>
|
|
// inline bool pretty_print(std::ostream& stream, std::shared_ptr<T>& value);
|
|
|
|
template <size_t N>
|
|
inline bool pretty_print(std::ostream &stream, const char (&value)[N]);
|
|
|
|
template <>
|
|
inline bool pretty_print(std::ostream &stream, const char *const &value);
|
|
|
|
template <typename... Ts>
|
|
inline bool pretty_print(std::ostream &stream, const std::tuple<Ts...> &value);
|
|
|
|
template <>
|
|
inline bool pretty_print(std::ostream &stream, const std::tuple<> &);
|
|
|
|
template <> inline bool pretty_print(std::ostream &stream, const time &);
|
|
|
|
template <typename T>
|
|
inline bool pretty_print(std::ostream &stream, const print_formatted<T> &value);
|
|
|
|
template <typename T>
|
|
inline bool pretty_print(std::ostream &stream, const print_type<T> &);
|
|
|
|
template <typename Enum>
|
|
inline typename std::enable_if<std::is_enum<Enum>::value, bool>::type
|
|
pretty_print(std::ostream &stream, Enum const &value);
|
|
|
|
inline bool pretty_print(std::ostream &stream, const std::string &value);
|
|
|
|
#if DBG_MACRO_CXX_STANDARD >= 17
|
|
|
|
inline bool pretty_print(std::ostream &stream, const std::string_view &value);
|
|
|
|
#endif
|
|
|
|
template <typename T1, typename T2>
|
|
inline bool pretty_print(std::ostream &stream, const std::pair<T1, T2> &value);
|
|
|
|
#if DBG_MACRO_CXX_STANDARD >= 17
|
|
|
|
template <typename T>
|
|
inline bool pretty_print(std::ostream &stream, const std::optional<T> &value);
|
|
|
|
template <typename... Ts>
|
|
inline bool pretty_print(std::ostream &stream,
|
|
const std::variant<Ts...> &value);
|
|
|
|
#endif
|
|
|
|
template <typename Container>
|
|
inline typename std::enable_if<detail::is_container<const Container &>::value,
|
|
bool>::type
|
|
pretty_print(std::ostream &stream, const Container &value);
|
|
|
|
// Specializations of "pretty_print"
|
|
|
|
template <typename T>
|
|
inline void pretty_print(std::ostream &stream, const T &value, std::true_type) {
|
|
stream << value;
|
|
}
|
|
|
|
template <typename T>
|
|
inline void pretty_print(std::ostream &, const T &, std::false_type) {
|
|
static_assert(detail::has_ostream_operator<const T &>::value,
|
|
"Type does not support the << ostream operator");
|
|
}
|
|
|
|
template <typename T>
|
|
inline typename std::enable_if<!detail::is_container<const T &>::value &&
|
|
!std::is_enum<T>::value,
|
|
bool>::type
|
|
pretty_print(std::ostream &stream, const T &value) {
|
|
pretty_print(stream, value,
|
|
typename detail::has_ostream_operator<const T &>::type{});
|
|
return true;
|
|
}
|
|
|
|
inline bool pretty_print(std::ostream &stream, const bool &value) {
|
|
stream << std::boolalpha << value;
|
|
return true;
|
|
}
|
|
|
|
inline bool pretty_print(std::ostream &stream, const char &value) {
|
|
const bool printable = value >= 0x20 && value <= 0x7E;
|
|
|
|
if (printable) {
|
|
stream << "'" << value << "'";
|
|
} else {
|
|
stream << "'\\x" << std::setw(2) << std::setfill('0') << std::hex
|
|
<< std::uppercase << (0xFF & value) << "'";
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename P>
|
|
inline bool pretty_print(std::ostream &stream, P *const &value) {
|
|
if (value == nullptr) {
|
|
stream << "nullptr";
|
|
} else {
|
|
stream << value;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename T, typename Deleter>
|
|
inline bool pretty_print(std::ostream &stream,
|
|
std::unique_ptr<T, Deleter> &value) {
|
|
pretty_print(stream, value.get());
|
|
return true;
|
|
}
|
|
|
|
// template <typename T>
|
|
// inline bool pretty_print(std::ostream& stream, std::shared_ptr<T>& value) {
|
|
// pretty_print(stream, value.get());
|
|
// stream << " (use_count = " << value.use_count() << ")";
|
|
// return true;
|
|
// }
|
|
|
|
template <size_t N>
|
|
inline bool pretty_print(std::ostream &stream, const char (&value)[N]) {
|
|
stream << value;
|
|
return false;
|
|
}
|
|
|
|
template <>
|
|
inline bool pretty_print(std::ostream &stream, const char *const &value) {
|
|
stream << '"' << value << '"';
|
|
return true;
|
|
}
|
|
|
|
template <size_t Idx> struct pretty_print_tuple {
|
|
template <typename... Ts>
|
|
static void print(std::ostream &stream, const std::tuple<Ts...> &tuple) {
|
|
pretty_print_tuple<Idx - 1>::print(stream, tuple);
|
|
stream << ", ";
|
|
pretty_print(stream, std::get<Idx>(tuple));
|
|
}
|
|
};
|
|
|
|
template <> struct pretty_print_tuple<0> {
|
|
template <typename... Ts>
|
|
static void print(std::ostream &stream, const std::tuple<Ts...> &tuple) {
|
|
pretty_print(stream, std::get<0>(tuple));
|
|
}
|
|
};
|
|
|
|
template <typename... Ts>
|
|
inline bool pretty_print(std::ostream &stream, const std::tuple<Ts...> &value) {
|
|
stream << "{";
|
|
pretty_print_tuple<sizeof...(Ts) - 1>::print(stream, value);
|
|
stream << "}";
|
|
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
inline bool pretty_print(std::ostream &stream, const std::tuple<> &) {
|
|
stream << "{}";
|
|
|
|
return true;
|
|
}
|
|
|
|
template <> inline bool pretty_print(std::ostream &stream, const time &) {
|
|
using namespace std::chrono;
|
|
|
|
const auto now = system_clock::now();
|
|
const auto us =
|
|
duration_cast<microseconds>(now.time_since_epoch()).count() % 1000000;
|
|
const auto hms = system_clock::to_time_t(now);
|
|
const std::tm *tm = std::localtime(&hms);
|
|
stream << "current time = " << std::put_time(tm, "%H:%M:%S") << '.'
|
|
<< std::setw(6) << std::setfill('0') << us;
|
|
|
|
return false;
|
|
}
|
|
|
|
// Converts decimal integer to binary string
|
|
template <typename T> std::string decimalToBinary(T n) {
|
|
const size_t length = 8 * sizeof(T);
|
|
std::string toRet;
|
|
toRet.resize(length);
|
|
|
|
for (size_t i = 0; i < length; ++i) {
|
|
const auto bit_at_index_i = static_cast<char>((n >> i) & 1);
|
|
toRet[length - 1 - i] = bit_at_index_i + '0';
|
|
}
|
|
|
|
return toRet;
|
|
}
|
|
|
|
template <typename T>
|
|
inline bool pretty_print(std::ostream &stream,
|
|
const print_formatted<T> &value) {
|
|
if (value.inner < 0) {
|
|
stream << "-";
|
|
}
|
|
stream << value.prefix();
|
|
|
|
// Print using setbase
|
|
if (value.base != 2) {
|
|
stream << std::setw(sizeof(T)) << std::setfill('0')
|
|
<< std::setbase(value.base) << std::uppercase;
|
|
|
|
if (value.inner >= 0) {
|
|
// The '+' sign makes sure that a uint_8 is printed as a number
|
|
stream << +value.inner;
|
|
} else {
|
|
using unsigned_type = typename std::make_unsigned<T>::type;
|
|
stream << +(static_cast<unsigned_type>(-(value.inner + 1)) + 1);
|
|
}
|
|
} else {
|
|
// Print for binary
|
|
if (value.inner >= 0) {
|
|
stream << decimalToBinary(value.inner);
|
|
} else {
|
|
using unsigned_type = typename std::make_unsigned<T>::type;
|
|
stream << decimalToBinary<unsigned_type>(
|
|
static_cast<unsigned_type>(-(value.inner + 1)) + 1);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
inline bool pretty_print(std::ostream &stream, const print_type<T> &) {
|
|
stream << type_name<T>();
|
|
|
|
stream << " [sizeof: " << sizeof(T) << " byte, ";
|
|
|
|
stream << "trivial: ";
|
|
if (std::is_trivial<T>::value) {
|
|
stream << "yes";
|
|
} else {
|
|
stream << "no";
|
|
}
|
|
|
|
stream << ", standard layout: ";
|
|
if (std::is_standard_layout<T>::value) {
|
|
stream << "yes";
|
|
} else {
|
|
stream << "no";
|
|
}
|
|
stream << "]";
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename Enum>
|
|
inline typename std::enable_if<std::is_enum<Enum>::value, bool>::type
|
|
pretty_print(std::ostream &stream, Enum const &value) {
|
|
using UnderlyingType = typename std::underlying_type<Enum>::type;
|
|
stream << static_cast<UnderlyingType>(value);
|
|
|
|
return true;
|
|
}
|
|
|
|
inline bool pretty_print(std::ostream &stream, const std::string &value) {
|
|
stream << '"' << value << '"';
|
|
return true;
|
|
}
|
|
|
|
#if DBG_MACRO_CXX_STANDARD >= 17
|
|
|
|
inline bool pretty_print(std::ostream &stream, const std::string_view &value) {
|
|
stream << '"' << std::string(value) << '"';
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
template <typename T1, typename T2>
|
|
inline bool pretty_print(std::ostream &stream, const std::pair<T1, T2> &value) {
|
|
stream << "{";
|
|
pretty_print(stream, value.first);
|
|
stream << ", ";
|
|
pretty_print(stream, value.second);
|
|
stream << "}";
|
|
return true;
|
|
}
|
|
|
|
#if DBG_MACRO_CXX_STANDARD >= 17
|
|
|
|
template <typename T>
|
|
inline bool pretty_print(std::ostream &stream, const std::optional<T> &value) {
|
|
if (value) {
|
|
stream << '{';
|
|
pretty_print(stream, *value);
|
|
stream << '}';
|
|
} else {
|
|
stream << "nullopt";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename... Ts>
|
|
inline bool pretty_print(std::ostream &stream,
|
|
const std::variant<Ts...> &value) {
|
|
stream << "{";
|
|
std::visit([&stream](auto &&arg) { pretty_print(stream, arg); }, value);
|
|
stream << "}";
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
template <typename Container>
|
|
inline typename std::enable_if<detail::is_container<const Container &>::value,
|
|
bool>::type
|
|
pretty_print(std::ostream &stream, const Container &value) {
|
|
stream << "{";
|
|
const size_t size = detail::size(value);
|
|
const size_t n = std::min(size_t{10}, size);
|
|
size_t i = 0;
|
|
using std::begin;
|
|
using std::end;
|
|
for (auto it = begin(value); it != end(value) && i < n; ++it, ++i) {
|
|
pretty_print(stream, *it);
|
|
if (i != n - 1) {
|
|
stream << ", ";
|
|
}
|
|
}
|
|
|
|
if (size > n) {
|
|
stream << ", ...";
|
|
stream << " size:" << size;
|
|
}
|
|
|
|
stream << "}";
|
|
return true;
|
|
}
|
|
|
|
template <typename T, typename... U> struct last {
|
|
using type = typename last<U...>::type;
|
|
};
|
|
|
|
template <typename T> struct last<T> { using type = T; };
|
|
|
|
template <typename... T> using last_t = typename last<T...>::type;
|
|
|
|
class DebugOutput {
|
|
public:
|
|
// Helper alias to avoid obscure type `const char* const*` in signature.
|
|
using expr_t = const char *;
|
|
|
|
DebugOutput(const char *filepath, int line, const char *function_name)
|
|
: m_use_colorized_output(isColorizedOutputEnabled()) {
|
|
std::string path = filepath;
|
|
const std::size_t path_length = path.length();
|
|
if (path_length > MAX_PATH_LENGTH) {
|
|
path = ".." +
|
|
path.substr(path_length - MAX_PATH_LENGTH, MAX_PATH_LENGTH);
|
|
}
|
|
std::stringstream ss;
|
|
ss << ansi(ANSI_DEBUG) << "[" << path << ":" << line << " ("
|
|
<< function_name << ")] " << ansi(ANSI_RESET);
|
|
m_location = ss.str();
|
|
}
|
|
|
|
template <typename... T>
|
|
auto print(std::initializer_list<expr_t> exprs,
|
|
std::initializer_list<std::string> types, T &&...values)
|
|
-> last_t<T...> {
|
|
if (exprs.size() != sizeof...(values)) {
|
|
std::cerr << m_location << ansi(ANSI_WARN)
|
|
<< "The number of arguments mismatch, please check "
|
|
"unprotected comma"
|
|
<< ansi(ANSI_RESET) << std::endl;
|
|
}
|
|
return print_impl(exprs.begin(), types.begin(),
|
|
std::forward<T>(values)...);
|
|
}
|
|
|
|
private:
|
|
template <typename T>
|
|
T &&print_impl(const expr_t *expr, const std::string *type, T &&value) {
|
|
const T &ref = value;
|
|
std::stringstream stream_value;
|
|
const bool print_expr_and_type = pretty_print(stream_value, ref);
|
|
|
|
std::stringstream output;
|
|
output << m_location;
|
|
if (print_expr_and_type) {
|
|
output << ansi(ANSI_EXPRESSION) << *expr << ansi(ANSI_RESET)
|
|
<< " = ";
|
|
}
|
|
output << ansi(ANSI_VALUE) << stream_value.str() << ansi(ANSI_RESET);
|
|
if (print_expr_and_type) {
|
|
output << " (" << ansi(ANSI_TYPE) << *type << ansi(ANSI_RESET)
|
|
<< ")";
|
|
}
|
|
output << std::endl;
|
|
std::cerr << output.str();
|
|
|
|
return std::forward<T>(value);
|
|
}
|
|
|
|
template <typename T, typename... U>
|
|
auto print_impl(const expr_t *exprs, const std::string *types, T &&value,
|
|
U &&...rest) -> last_t<T, U...> {
|
|
print_impl(exprs, types, std::forward<T>(value));
|
|
return print_impl(exprs + 1, types + 1, std::forward<U>(rest)...);
|
|
}
|
|
|
|
const char *ansi(const char *code) const {
|
|
if (m_use_colorized_output) {
|
|
return code;
|
|
} else {
|
|
return ANSI_EMPTY;
|
|
}
|
|
}
|
|
|
|
const bool m_use_colorized_output;
|
|
|
|
std::string m_location;
|
|
|
|
static constexpr std::size_t MAX_PATH_LENGTH = 20;
|
|
|
|
static constexpr const char *const ANSI_EMPTY = "";
|
|
static constexpr const char *const ANSI_DEBUG = "\x1b[02m";
|
|
static constexpr const char *const ANSI_WARN = "\x1b[33m";
|
|
static constexpr const char *const ANSI_EXPRESSION = "\x1b[36m";
|
|
static constexpr const char *const ANSI_VALUE = "\x1b[01m";
|
|
static constexpr const char *const ANSI_TYPE = "\x1b[32m";
|
|
static constexpr const char *const ANSI_RESET = "\x1b[0m";
|
|
};
|
|
|
|
// Identity function to suppress "-Wunused-value" warnings in DBG_MACRO_DISABLE
|
|
// mode
|
|
template <typename T> T &&identity(T &&t) { return std::forward<T>(t); }
|
|
|
|
template <typename T, typename... U>
|
|
auto identity(T &&, U &&...u) -> last_t<U...> {
|
|
return identity(std::forward<U>(u)...);
|
|
}
|
|
|
|
} // namespace dbg
|
|
|
|
#ifndef DBG_MACRO_DISABLE
|
|
|
|
// Force expanding argument with commas for MSVC, ref:
|
|
// https://stackoverflow.com/questions/35210637/macro-expansion-argument-with-commas
|
|
// Note that "args" should be a tuple with parentheses, such as "(e1, e2, ...)".
|
|
#define DBG_IDENTITY(x) x
|
|
#define DBG_CALL(fn, args) DBG_IDENTITY(fn args)
|
|
|
|
#define DBG_CAT_IMPL(_1, _2) _1##_2
|
|
#define DBG_CAT(_1, _2) DBG_CAT_IMPL(_1, _2)
|
|
|
|
#define DBG_16TH_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
|
|
_14, _15, _16, ...) \
|
|
_16
|
|
#define DBG_16TH(args) DBG_CALL(DBG_16TH_IMPL, args)
|
|
#define DBG_NARG(...) \
|
|
DBG_16TH( \
|
|
(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
|
|
|
|
// DBG_VARIADIC_CALL(fn, data, e1, e2, ...) => fn_N(data, (e1, e2, ...))
|
|
#define DBG_VARIADIC_CALL(fn, data, ...) \
|
|
DBG_CAT(fn##_, DBG_NARG(__VA_ARGS__))(data, (__VA_ARGS__))
|
|
|
|
// (e1, e2, e3, ...) => e1
|
|
#define DBG_HEAD_IMPL(_1, ...) _1
|
|
#define DBG_HEAD(args) DBG_CALL(DBG_HEAD_IMPL, args)
|
|
|
|
// (e1, e2, e3, ...) => (e2, e3, ...)
|
|
#define DBG_TAIL_IMPL(_1, ...) (__VA_ARGS__)
|
|
#define DBG_TAIL(args) DBG_CALL(DBG_TAIL_IMPL, args)
|
|
|
|
#define DBG_MAP_1(fn, args) DBG_CALL(fn, args)
|
|
#define DBG_MAP_2(fn, args) fn(DBG_HEAD(args)), DBG_MAP_1(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_3(fn, args) fn(DBG_HEAD(args)), DBG_MAP_2(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_4(fn, args) fn(DBG_HEAD(args)), DBG_MAP_3(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_5(fn, args) fn(DBG_HEAD(args)), DBG_MAP_4(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_6(fn, args) fn(DBG_HEAD(args)), DBG_MAP_5(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_7(fn, args) fn(DBG_HEAD(args)), DBG_MAP_6(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_8(fn, args) fn(DBG_HEAD(args)), DBG_MAP_7(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_9(fn, args) fn(DBG_HEAD(args)), DBG_MAP_8(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_10(fn, args) fn(DBG_HEAD(args)), DBG_MAP_9(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_11(fn, args) fn(DBG_HEAD(args)), DBG_MAP_10(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_12(fn, args) fn(DBG_HEAD(args)), DBG_MAP_11(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_13(fn, args) fn(DBG_HEAD(args)), DBG_MAP_12(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_14(fn, args) fn(DBG_HEAD(args)), DBG_MAP_13(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_15(fn, args) fn(DBG_HEAD(args)), DBG_MAP_14(fn, DBG_TAIL(args))
|
|
#define DBG_MAP_16(fn, args) fn(DBG_HEAD(args)), DBG_MAP_15(fn, DBG_TAIL(args))
|
|
|
|
// DBG_MAP(fn, e1, e2, e3, ...) => fn(e1), fn(e2), fn(e3), ...
|
|
#define DBG_MAP(fn, ...) DBG_VARIADIC_CALL(DBG_MAP, fn, __VA_ARGS__)
|
|
|
|
#define DBG_STRINGIFY_IMPL(x) #x
|
|
#define DBG_STRINGIFY(x) DBG_STRINGIFY_IMPL(x)
|
|
|
|
#define DBG_TYPE_NAME(x) dbg::type_name<decltype(x)>()
|
|
|
|
#define dbg(...) \
|
|
dbg::DebugOutput(__FILE__, __LINE__, __func__) \
|
|
.print({DBG_MAP(DBG_STRINGIFY, __VA_ARGS__)}, \
|
|
{DBG_MAP(DBG_TYPE_NAME, __VA_ARGS__)}, __VA_ARGS__)
|
|
#else
|
|
#define dbg(...) dbg::identity(__VA_ARGS__)
|
|
#endif // DBG_MACRO_DISABLE
|
|
|
|
#endif // DBG_MACRO_DBG_H
|