Skip to content

Instantly share code, notes, and snippets.

@naskya
Last active November 30, 2023 06:08
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save naskya/1e5e5cd269cfe16a76988378a60e2ca3 to your computer and use it in GitHub Desktop.
Save naskya/1e5e5cd269cfe16a76988378a60e2ca3 to your computer and use it in GitHub Desktop.
/* last update: 2022-08-03 */
#ifndef DEBUG_PRINT_HPP
#define DEBUG_PRINT_HPP
#define INCLUDED(n) ((defined _GLIBCXX_##n) || (defined _LIBCPP_##n))
#if __cplusplus < 201703L
# warning Please use C++17 (or later version).
#endif
#if !INCLUDED(ALGORITHM)
# warning Please include <algorithm> before including debug_print.hpp.
#endif
#if !INCLUDED(CCTYPE)
# warning Please include <cctype> before including debug_print.hpp.
#endif
#if !INCLUDED(IOSTREAM)
# warning Please include <iostream> before including debug_print.hpp.
#endif
#if !INCLUDED(ITERATOR)
# warning Please include <iterator> before including debug_print.hpp.
#endif
#if !INCLUDED(STRING_VIEW)
# warning Please include <string_view> before including debug_print.hpp.
#endif
#if !INCLUDED(TYPE_TRAITS)
# warning Please include <type_traits> before including debug_print.hpp.
#endif
namespace debug_print {
std::ostream& os = std::cerr;
template <class Tp> auto has_cbegin(int) -> decltype(std::cbegin(std::declval<Tp>()), std::true_type {});
template <class Tp> auto has_cbegin(...) -> std::false_type;
template <class Tp> auto has_value_type(int) -> decltype(std::declval<typename Tp::value_type>(), std::true_type {});
template <class Tp> auto has_value_type(...) -> std::false_type;
template <class Tp>[[maybe_unused]] constexpr bool is_iterable_container_v = decltype(has_cbegin<Tp>(int {}))::value;
template <class Tp>[[maybe_unused]] constexpr bool is_container_v = decltype(has_value_type<Tp>(int {}))::value
|| is_iterable_container_v<Tp>;
template <> [[maybe_unused]] constexpr bool is_iterable_container_v<std::string_view> = false;
template <> [[maybe_unused]] constexpr bool is_container_v<std::string_view> = false;
#if INCLUDED(STRING)
template <> [[maybe_unused]] constexpr bool is_iterable_container_v<std::string> = false;
template <> [[maybe_unused]] constexpr bool is_container_v<std::string> = false;
#endif
template <class Tp, class... Ts> struct first_element { using type = Tp; };
template <class... Ts> using first_t = typename first_element<Ts...>::type;
template <class Tp, std::enable_if_t<!decltype(has_value_type<Tp>(int {}))::value, std::nullptr_t> = nullptr>
auto check_elem(int) -> decltype(*std::cbegin(std::declval<Tp>()));
template <class Tp, std::enable_if_t<decltype(has_value_type<Tp>(int {}))::value, std::nullptr_t> = nullptr>
auto check_elem(int) -> typename Tp::value_type;
template <class Tp>
auto check_elem(...) -> void;
template <class Tp> using elem_t = decltype(check_elem<Tp>(int {}));
template <class Tp> [[maybe_unused]] constexpr bool is_multidim_container_v = is_container_v<Tp>
&& is_container_v<elem_t<Tp>>;
template <class Tp> std::enable_if_t<!is_container_v<Tp>> out(const Tp&);
void out(const char&);
void out(const char*);
void out(const std::string_view&);
#if INCLUDED(STRING)
void out(const std::string&);
#endif
#ifdef __SIZEOF_INT128__
void out(const __int128&);
void out(const unsigned __int128&);
#endif
template <class Tp1, class Tp2> void out(const std::pair<Tp1, Tp2>&);
#if INCLUDED(TUPLE)
template <class... Ts> void out(const std::tuple<Ts...>&);
#endif
#if INCLUDED(STACK)
template <class... Ts> void out(std::stack<Ts...>);
#endif
#if INCLUDED(QUEUE)
template <class... Ts> void out(std::queue<Ts...>);
template <class... Ts> void out(std::priority_queue<Ts...>);
#endif
template <class C> std::enable_if_t<is_iterable_container_v<C>> out(const C&);
template <class Tp> std::enable_if_t<!is_container_v<Tp>> out(const Tp& arg) {
os << arg;
}
void out(const char& arg) {
os << '\'' << arg << '\'';
}
void out(const char* arg) {
os << '\"' << arg << '\"';
}
void out(const std::string_view& arg) {
os << '\"' << arg << '\"';
}
#if INCLUDED(STRING)
void out(const std::string& arg) {
os << '\"' << arg << '\"';
}
#endif
#ifdef __SIZEOF_INT128__
void out(const __int128& arg) {
int sign = (arg < 0) ? (-1) : 1;
if (sign == -1)
os << '-';
__int128 base = sign;
while (sign * arg >= sign * base * 10)
base *= 10;
while (base) {
os << static_cast<char>('0' + (arg / base % 10));
base /= 10;
}
}
void out(const unsigned __int128& arg) {
unsigned __int128 base = 1;
while (arg >= base * 10)
base *= 10;
while (base) {
os << static_cast<char>('0' + (arg / base % 10));
base /= 10;
}
}
#endif
template <class Tp1, class Tp2> void out(const std::pair<Tp1, Tp2>& arg) {
os << '(';
out(arg.first);
os << ", ";
out(arg.second);
os << ')';
}
#if INCLUDED(TUPLE)
template <class T, std::size_t... Is> void print_tuple(const T& arg, std::index_sequence<Is...>) {
static_cast<void>(((os << (Is == 0 ? "" : ", "), out(std::get<Is>(arg))), ...));
}
template <class... Ts> void out(const std::tuple<Ts...>& arg) {
os << '(';
print_tuple(arg, std::make_index_sequence<sizeof...(Ts)>());
os << ')';
}
#endif
#if INCLUDED(STACK)
template <class... Ts> void out(std::stack<Ts...> arg) {
if (arg.empty()) {
os << "<empty stack>";
return;
}
os << "[ ";
while (!arg.empty()) {
out(arg.top());
os << ' ';
arg.pop();
}
os << ']';
}
#endif
#if INCLUDED(QUEUE)
template <class... Ts> void out(std::queue<Ts...> arg) {
if (arg.empty()) {
os << "<empty queue>";
return;
}
os << "[ ";
while (!arg.empty()) {
out(arg.front());
os << ' ';
arg.pop();
}
os << ']';
}
template <class... Ts> void out(std::priority_queue<Ts...> arg) {
if (arg.empty()) {
os << "<empty priority_queue>";
return;
}
os << "[ ";
while (!arg.empty()) {
out(arg.top());
os << ' ';
arg.pop();
}
os << ']';
}
#endif
template <class Container>
std::enable_if_t<is_iterable_container_v<Container>> out(const Container& arg) {
if (std::distance(std::cbegin(arg), std::cend(arg)) == 0) {
os << "<empty container>";
return;
}
os << "[ ";
std::for_each(std::cbegin(arg), std::cend(arg), [](const elem_t<Container>& elem) {
out(elem);
os << ' ';
});
os << ']';
}
template <class Tp> std::enable_if_t<!is_multidim_container_v<Tp>>
print(std::string_view name, const Tp& arg) {
os << name << ": ";
out(arg);
if constexpr (is_container_v<Tp>)
os << '\n';
}
template <class Tp> std::enable_if_t<is_multidim_container_v<Tp>>
print(std::string_view name, const Tp& arg) {
os << name << ": ";
if (std::distance(std::cbegin(arg), std::cend(arg)) == 0) {
os << "<empty multidimensional container>\n";
return;
}
std::for_each(std::cbegin(arg), std::cend(arg),
[&name, is_first_elem = true](const elem_t<Tp>& elem) mutable {
if (is_first_elem)
is_first_elem = false;
else
for (std::size_t i = 0; i < name.length() + 2; i++)
os << ' ';
out(elem);
os << '\n';
});
}
template <class Tp, class... Ts> void multi_print(std::string_view names, const Tp& arg, const Ts&... args) {
if constexpr (sizeof...(Ts) == 0) {
names.remove_suffix(
std::distance(
names.crbegin(),
std::find_if_not(names.crbegin(), names.crend(),
[](const char c) { return std::isspace(c); })
)
);
print(names, arg);
if constexpr (!is_container_v<Tp>)
os << '\n';
} else {
std::size_t comma_pos = 0;
for (std::size_t i = 0, paren_depth = 0, inside_quote = false; i < names.length(); i++) {
if (!inside_quote && paren_depth == 0 && i > 0 && names[i - 1] != '\'' && names[i] == ',') {
comma_pos = i;
break;
}
if (names[i] == '\"' || names[i] == '\'') {
if (i > 0 && names[i - 1] == '\\') continue;
inside_quote ^= true;
}
if (!inside_quote && names[i] == '(' && (i == 0 || names[i - 1] != '\''))
paren_depth++;
if (!inside_quote && names[i] == ')' && (i == 0 || names[i - 1] != '\''))
paren_depth--;
}
const std::size_t first_varname_length = comma_pos - std::distance(
names.crend() - comma_pos,
std::find_if_not(
names.crend() - comma_pos, names.crend(),
[](const char c) { return std::isspace(c); }
)
);
print(names.substr(0, first_varname_length), arg);
if constexpr (!is_container_v<Tp>) {
if constexpr (is_container_v<first_t<Ts...>>)
os << '\n';
else
os << " | ";
}
const std::size_t next_varname_begins_at = std::distance(
names.cbegin(),
std::find_if_not(
names.cbegin() + comma_pos + 1, names.cend(),
[](const char c) { return std::isspace(c); }
)
);
names.remove_prefix(next_varname_begins_at);
multi_print(names, args...);
}
}
} // namespace debug_print
#undef INCLUDED
#endif // DEBUG_PRINT_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment