/********************************************************************************************************************** * * @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 CPPUtils__pretty_print_h__ #define CPPUtils__pretty_print_h__ #include #include #include #include #include #include #include namespace CPPUtils { /// @addtogroup pretty_print Pretty Print /// @{ /// @name Helper Templates /// These are not directly tested. /// @{ /// @brief This SFINAE struct is used to select the correct wide or narrow string based the TChar. /// @tparam TChar The character type of the string we want. This should either be char or wchar_t. template struct StringTraits; /// @brief This SFINAE struct is use to select a narrow string. template <> struct StringTraits { static constexpr const char *Literal(const char *narrow, const wchar_t *wide) { return narrow; } }; /// @brief This SFINAE struct is used to select a wide string. template <> struct StringTraits { static constexpr const wchar_t *Literal(const char *narrow, const wchar_t *wide) { return wide; } }; /// @brief This SFINAE struct is used to help select container like types. /// @tparam T The potential container type. 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); }; /// @} /// @name Escape for Printing /// These methods all escape different kinds of strings for printing. Currently they replace the ansi escape character /// with a printable version of its code "\033". /// @{ /// @brief This function escapes a char* or wchar_t* for printing to cout or wcout. /// /// It replaces the escape character with a its code. /// @tparam TChar The type of character of our string. This should be dertermined automatically based on the input type. /// @param text The text to escape. /// @return The escaped string. template std::basic_string EscapeForPrinting(const TChar *text); // std::string and std::wstring /// @brief This function escapes a std::string or std::wstring for printing to cout or wcout. /// @tparam TChar The type of character of our string. This should be dertermined automatically based on the input type. /// @tparam TTraits The char_traits type of our string. This should be determined automatically based on the input type. /// @param text The text to escape. /// @return The escaped string. template std::basic_string EscapeForPrinting(const std::basic_string &text); // std::string_view and std::wstring_view /// @brief This function escapes a std::string_view or std::wstring_view for printing to cout or wcout. /// @tparam TChar The type of character of our string. This should be dertermined automatically based on the input type. /// @tparam TTraits The char_traits type of our string. This should be determined automatically based on the input type. /// @param text The text to escape. /// @return The escaped string. template std::basic_string EscapeForPrinting(const std::basic_string_view &text); /// @} /// @name Pretty Print /// These functions attempt to print friendlier versions of strings and objects that don't have operator<< defined for /// them. /// @{ /// @brief This function prints a pointer. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @param os The output stream to write to. /// @param pointer The pointer to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, const void *pointer); /// @brief This function prints a const char* or const wchar_t* to the output stream. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @param os The output stream to write to. /// @param text The text to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, const TChar *text); /// @brief This function prints a std::string or std::wstring to the output stream. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @param os The output stream to write to. /// @param text The text to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, const std::basic_string &text); /// @brief This function prints a std::string_view or std::wstring_view to the output stream. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @param os The output stream to write to. /// @param text The text to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, const std::basic_string_view &text); /// @brief This function prints a std::tuple to the output stream. /// /// The output format for an empty tuple is "[]". For a tuple of (1, "two", 3.0) the output format is "[ 1, "two", 3.0 /// ]". /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam ...TArgs The types of the tuple. /// @param os The output stream to write to. /// @param tuple The tuple to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, const std::tuple &tuple); /// @brief This function prints any stl-like container to the output stream. /// /// It works with any class that has begin and end methods that return iterators. /// /// The output format for an empty container is "[]". For a (vector){1, 2, 3} the output format is "[ 1, 2, 3 ]". /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam TContainer The type of the container to print. /// @tparam Ignore this parameter it has a default value. /// @tparam Ignore this parameter it has a default value. /// @param os The output stream to write to. /// @param container The container to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, TContainer container); /// @brief This function prints an initializer list to the output stream. /// /// The output format for a list {"hello", "world"} is "[ "hello", "world" ]". Unfortunately empty initializer lists are /// treated as null pointers and will either print "null" or however your platform prints pointers. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam TItem The type of the item in the initializer list. /// @param os The output stream to write to. /// @param list The initializer_list to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, std::initializer_list list); /// @brief This function prints STL queues to the output stream. /// /// The output format for an empty queue is "[]". The output format for a queue of 5,1,3 is "[ 5, 1, 3 ]". /// /// The queue is copied and the copy is emptied. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam TItem The type of the item in the initializer list. /// @param os The output stream to write to. /// @param queue The queue to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, std::queue queue); /// @brief This function prints STL pairs to the output stream. /// /// The output format for a pair of (3, "Kansas") is "(3, "Kansas")". /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam TFirst The type of the first item in the pair. /// @tparam TSecond The type of the second item in the pair. /// @param os The output stream to write to. /// @param value The pair to print. /// @return The output stream for chaining. template auto &PrettyPrint(std::basic_ostream &os, std::pair value); /// @brief This function prints other items. It just pipes the item to the output stream. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam TItem The type of the item to print. /// @param os The output stream to write to. /// @param item The item to print. /// @return The output stream for chaining. template ::value>::type * = nullptr> auto &PrettyPrint(std::basic_ostream &os, TItem item); /// @} /// @name Pretty Print with Separator /// These functions pretty print items with a separator. /// @{ /// @brief This function prints varargs with a const char* or const wchar_t* separator between each pair. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam ...Args The types of the arguments to print. /// @param os The output stream to write to. /// @param separator The separator to use between the items. /// @param ...args The items to print. /// @return The output stream for chaining. template auto &PrettyPrintWithSeparator(std::basic_ostream &os, const TChar *separator, Args &&...args); /// @brief This function prints varargs with a std::string or std::wstring separator between each pair. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam ...Args The types of the arguments to print. /// @param os The output stream to write to. /// @param separator The separator to use between the items. /// @param ...args The items to print. /// @return The output stream for chaining. template auto &PrettyPrintWithSeparator(std::basic_ostream &os, std::basic_string separator, TArgs &&...args); /// @brief This function prints varargs with a std::string_view or std::wstring_view separator between each pair. /// @tparam TChar The character type of our stream. /// @tparam TTraits The character_traits type of our stream. /// @tparam ...Args The types of the arguments to print. /// @param os The output stream to write to. /// @param separator The separator to use between the items. /// @param ...args The items to print. /// @return The output stream for chaining. template auto &PrettyPrintWithSeparator(std::basic_ostream &os, std::basic_string_view separator, TArgs &&...args); /// @} // const char* and const wchar_t* 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 and std::wstring 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 and std::wstring_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* and const wchar_t* template auto &PrettyPrint(std::basic_ostream &os, const TChar *item) { os << StringTraits::Literal("\"", L"\"") << EscapeForPrinting(item) << StringTraits::Literal("\"", L"\""); return os; } // std::string and std::wstring template auto &PrettyPrint(std::basic_ostream &os, const std::basic_string &text) { os << StringTraits::Literal("\"", L"\"") << EscapeForPrinting(text) << StringTraits::Literal("\"", L"\""); return os; } // std::string_view and std::wstring_view template auto &PrettyPrint(std::basic_ostream &os, const std::basic_string_view &text) { os << StringTraits::Literal("\"", L"\"") << EscapeForPrinting(text) << 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; } // STL 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; } // std::initializer_list template auto &PrettyPrint(std::basic_ostream &os, std::initializer_list list) { if (list.size() <= 0) { os << StringTraits::Literal("[]", L"[]"); return os; } os << StringTraits::Literal("[ ", L"[ "); for (auto it = std::begin(list); it != std::end(list); it++) { if (it != std::begin(list)) { os << StringTraits::Literal(", ", L", "); } PrettyPrint(os, *it); } os << StringTraits::Literal(" ]", L" ]"); return os; } // std::queue 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; } // std::pair 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 CPPUtils__pretty_print_h__