Last active
November 30, 2023 06:08
-
-
Save naskya/1e5e5cd269cfe16a76988378a60e2ca3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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