/********************************************************************************************************************** * * * @file pretty_print.h * * * * @brief Declares function templates for printing objects in a friendlier manner. * * This specifically helps for printing stl containers, tuples, pairs, and queues as well as quoting strings * * contained within them. * * @copyright Copyright (C) 2023 by Tom Hicks * * * * Licensed under the MIT license see below for details. * * * * MIT License * * * * Copyright (c) 2023 Tom Hicks * * * * 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 CPP_UTILS_pretty_print_h__ #define CPP_UTILS_pretty_print_h__ #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__