#ifndef CPP_UTILS_pretty_print_h__ #define CPP_UTILS_pretty_print_h__ /*************************************************************************************** * @file pretty_print.cpp * * * * @brief Defines functions for printing objects in a frendlier manner. * * @copyright * * Copyright 2023 Tom Hicks * * Licensed under the MIT license see the LICENSE file for details. * ***************************************************************************************/ #include #include #include #include #include #include #include /** \addtogroup Pretty Print * @{ */ namespace CPPUtils { //////////////////////////////////////////////////////////////////////////////// // Helper templates - These are not directly tested. template struct StringTraits; template <> struct StringTraits { static constexpr const char* Literal(const char* narrow, const wchar_t* wide) { return narrow; } }; template <> struct StringTraits { static constexpr const wchar_t* Literal(const char* narrow, const wchar_t* wide) { return wide; } }; template struct is_container { template static constexpr bool test(decltype(std::begin(std::declval()))*) { return true; } template static constexpr bool test(...) { return false; } static constexpr bool value = test(nullptr); }; //////////////////////////////////////////////////////////////////////////////// // Forward Declarations // const char* and const wchar_t* template std::basic_string EscapeForPrinting(const TChar* text); // std::string and std::wstring template std::basic_string EscapeForPrinting(const std::basic_string& text); // std::string_view and std::wstring_view template std::basic_string EscapeForPrinting(const std::basic_string_view& text); // pointers template auto& PrettyPrint(std::basic_ostream& os, const void* pointer); // const char* and const wchar_t* template auto& PrettyPrint(std::basic_ostream& os, const TChar* item); // std::string and std::wstring template auto& PrettyPrint(std::basic_ostream& os, const std::basic_string& item); // std::string_view and std::wstring_view template auto& PrettyPrint(std::basic_ostream& os, const std::basic_string_view& item); // std::tuple template auto& PrettyPrint(std::basic_ostream& os, const std::tuple& tuple); // Containers (anything with a begin and end iterator) template auto& PrettyPrint(std::basic_ostream& os, TContainer container); // Initializer Lists template auto& PrettyPrint(std::basic_ostream& os, std::initializer_list container); // queues template auto& PrettyPrint(std::basic_ostream& os, std::queue queue); // pairs template auto& PrettyPrint(std::basic_ostream& os, std::pair value); // Catch-all for everything else. Just print it and hope that works. template ::value>::type* = nullptr> auto& PrettyPrint(std::basic_ostream& os, TItem item); // Prints varargs with a const char* or const wchar_t* separator between each pair. template auto& PrettyPrintWithSeparator(std::basic_ostream& os, const TChar* separator, Args&&... args); // Prints varargs with a std::string or std::wstring separator. template auto& PrettyPrintWithSeparator(std::basic_ostream& os, std::basic_string separator, TArgs&&... args); // Prints varargs with a std::string_view or std::wstring_view separator. template auto& PrettyPrintWithSeparator(std::basic_ostream& os, std::basic_string_view separator, TArgs&&... args); //////////////////////////////////////////////////////////////////////////////// // Actual implementations // const char* template std::basic_string EscapeForPrinting(const TChar* text) { std::regex regex = std::regex(StringTraits::Literal("\033", L"\033")); const TChar* replace = StringTraits::Literal("\\033", L"\\033"); return std::regex_replace(text, regex, replace); } // std::string template std::basic_string EscapeForPrinting(const std::basic_string& text) { std::regex regex = std::regex(StringTraits::Literal("\033", L"\033")); std::basic_string replace = StringTraits::Literal("\\033", L"\\033"); return std::regex_replace(text, regex, replace); } // std::string_view template std::basic_string EscapeForPrinting(const std::basic_string_view& text) { std::basic_string text_as_string = std::basic_string(text); std::regex regex = std::regex(StringTraits::Literal("\033", L"\033")); std::basic_string replace = StringTraits::Literal("\\033", L"\\033"); return std::regex_replace(text_as_string, regex, replace); } // pointers template auto& PrettyPrint(std::basic_ostream& os, const void* pointer) { if (pointer == nullptr) { os << StringTraits::Literal("null", L"null"); } else { os << pointer; } return os; } // const char*. template auto& PrettyPrint(std::basic_ostream& os, const TChar* item) { os << StringTraits::Literal("\"", L"\"") << EscapeForPrinting(item) << StringTraits::Literal("\"", L"\""); return os; } // std::string. template auto& PrettyPrint(std::basic_ostream& os, const std::basic_string& item) { os << StringTraits::Literal("\"", L"\"") << EscapeForPrinting(item) << StringTraits::Literal("\"", L"\""); return os; } // std::string_view. template auto& PrettyPrint(std::basic_ostream& os, const std::basic_string_view& item) { os << StringTraits::Literal("\"", L"\"") << EscapeForPrinting(item) << StringTraits::Literal("\"", L"\""); return os; } // std::tuple template auto& PrettyPrint(std::basic_ostream& os, const std::tuple& tuple) { std::apply( [&os](auto&&... args) { if (sizeof...(TArgs) == 0) { os << StringTraits::Literal("[]", L"[]]"); return; } size_t n = 0; os << StringTraits::Literal("[ ", L"[ "); ((PrettyPrint(os, args) << (++n != sizeof...(TArgs) ? StringTraits::Literal(", ", L", ") : StringTraits::Literal("", L""))), ...); os << StringTraits::Literal(" ]", L" ]"); }, tuple); return os; } // Containers template ())), typename = decltype(std::end(std::declval())), typename = typename TContainer::value_type> auto& PrettyPrint(std::basic_ostream& os, TContainer container) { if (container.size() <= 0) { os << StringTraits::Literal("[]", L"[]"); } else { os << StringTraits::Literal("[ ", L"[ "); for (auto it = std::begin(container); it != std::end(container); it++) { if (it != std::begin(container)) { os << StringTraits::Literal(", ", L", "); } PrettyPrint(os, *it); } os << StringTraits::Literal(" ]", L" ]"); } return os; } // initializer_lists template auto& PrettyPrint(std::basic_ostream& os, std::initializer_list container) { if (container.size() <= 0) { os << StringTraits::Literal("[]", L"[]"); return os; } os << StringTraits::Literal("[ ", L"[ "); for (auto it = std::begin(container); it != std::end(container); it++) { if (it != std::begin(container)) { os << StringTraits::Literal(", ", L", "); } PrettyPrint(os, *it); } os << StringTraits::Literal(" ]", L" ]"); return os; } // queues template auto& PrettyPrint(std::basic_ostream& os, std::queue queue) { if (queue.empty()) { os << StringTraits::Literal("[]", L"[]"); return os; } os << StringTraits::Literal("[ ", L"[ "); PrettyPrint(os, queue.front()); queue.pop(); while (!queue.empty()) { os << StringTraits::Literal(", ", L", "); PrettyPrint(os, queue.front()); queue.pop(); } os << StringTraits::Literal(" ]", L" ]"); return os; } // pairs template auto& PrettyPrint(std::basic_ostream& os, std::pair value) { os << StringTraits::Literal("(", L"("); PrettyPrint(os, value.first); os << StringTraits::Literal(", ", L", "); PrettyPrint(os, value.second); os << StringTraits::Literal(")", L")"); return os; } // Catch-all for everything else. template ::value>::type*> auto& PrettyPrint(std::basic_ostream& os, TItem item) { os << item; return os; } // Prints args with separator between them. const char* separator. template auto& PrettyPrintWithSeparator(std::basic_ostream& os, const TChar* separator, TArgs&&... args) { if (sizeof...(TArgs) == 0) { os << StringTraits::Literal("", L""); return os; } size_t n = 0; ((PrettyPrint(os, args) << (++n != sizeof...(TArgs) ? separator : StringTraits::Literal("", L""))), ...); return os; } // Prints args with separator between them. std::string separator. template auto& PrettyPrintWithSeparator(std::basic_ostream& os, std::basic_string separator, TArgs&&... args) { if (sizeof...(TArgs) == 0) { os << StringTraits::Literal("", L""); return os; } size_t n = 0; ((PrettyPrint(os, args) << (++n != sizeof...(TArgs) ? separator : StringTraits::Literal("", L""))), ...); return os; } // Prints args with separator between them. std::string_view separator. template auto& PrettyPrintWithSeparator(std::basic_ostream& os, std::basic_string_view separator, TArgs&&... args) { if (sizeof...(TArgs) == 0) { os << StringTraits::Literal("", L""); return os; } size_t n = 0; ((PrettyPrint(os, args) << (++n != sizeof...(TArgs) ? separator : StringTraits::Literal("", L""))), ...); return os; } } // End namespace CPPUtils /** @}*/ #endif // End !defined CPP_UTILS_pretty_print_h__