#pragma once #include #include #include #include #include #include namespace llarp { /// simple guard class to restore stream flags. struct FormatFlagsGuard { std::ios_base& m_base; std::ios_base::fmtflags m_flags; FormatFlagsGuard(std::ios_base& base) : m_base(base), m_flags(base.flags()) {} ~FormatFlagsGuard() { m_base.flags(m_flags); } }; /// A general-purpose, stateful printer class. class Printer { private: std::ostream& m_stream; const int m_level; const int m_levelPlusOne; const bool m_suppressIndent; const int m_spaces; public: template using PrintFunction = std::function; /// Create a printer. /// - level: the indentation level to use. If negative, suppress indentation /// on the first line. /// - spaces: the number of spaces to indent. If negative, put all output on /// a single line Printer(std::ostream& stream, int level, int spaces); ~Printer(); /// Print the given `data` to the stream, using the following strategies: /// - If `Type` is fundamental, print to stream /// - If `Type` is a C-style array (and not a char array), print each /// element to the stream /// - If `Type` is a `void *`, `const void *` or function pointer, and not /// null, print in hex format or print "null". /// - If `Type` is a `char *`, a `const char *`, a C-style char array, a /// `std::string` or `std::string_view` print the string wrapped in `"`. /// - If `Type` is a pointer type, print the pointer, followed by the value /// if not-null. /// - If `Type` is a pair/tuple type, print the elements of the tuple. /// - If `Type` has STL-style iterators, print all elements in the /// container. /// - If `Type` is any other type, call the `print` method on that type. template void printAttribute(std::string_view name, const Type& value) const; template void printAttributeAsHex(std::string_view name, const Type& value) const; template void printAttribute(std::string_view name, const InputIt& begin, const InputIt& end) const; template void printValue(const Type& value) const; template void printValue(const InputIt& begin, const InputIt& end) const; template void printForeignAttribute( std::string_view name, const Type& value, const PrintFunction& printFunction) const; template void printForeignValue(const Type& value, const PrintFunction& printFunction) const; void printHexAddr(std::string_view name, const void* address) const; void printHexAddr(const void* address) const; template void printOrNull(std::string_view name, const Type& address) const; template void printOrNull(const Type& address) const; private: void printIndent() const; }; /// helper struct struct PrintHelper { template static void print(std::ostream& stream, const Type& value, int level, int spaces); template static void print(std::ostream& stream, const InputIt& begin, const InputIt& end, int level, int spaces); // Specialisations // Fundamental types static void printType( std::ostream& stream, char value, int level, int spaces, traits::select::Case); static void printType( std::ostream& stream, bool value, int level, int spaces, traits::select::Case); template static void printType( std::ostream& stream, Type value, int level, int spaces, traits::select::Case); template static void printType( std::ostream& stream, Type value, int level, int spaces, traits::select::Case); // Function types template static void printType( std::ostream& stream, Type value, int level, int spaces, traits::select::Case); // Pointer types static void printType( std::ostream& stream, const char* value, int level, int spaces, traits::select::Case); static void printType( std::ostream& stream, const void* value, int level, int spaces, traits::select::Case); template static void printType( std::ostream& stream, const Type* value, int level, int spaces, traits::select::Case); template static void printType( std::ostream& stream, const Type* value, int level, int spaces, traits::select::Case); // Container types static void printType( std::ostream& stream, const std::string& value, int level, int spaces, traits::select::Case); static void printType( std::ostream& stream, const std::string_view& value, int level, int spaces, traits::select::Case); template static void printType( std::ostream& stream, const Type& value, int level, int spaces, traits::select::Case); // Utility types template static void printType( std::ostream& stream, const std::pair& value, int level, int spaces, traits::select::Case<>); template static void printType( std::ostream& stream, const std::tuple& value, int level, int spaces, traits::select::Case<>); // Default type template static void printType( std::ostream& stream, const Type& value, int level, int spaces, traits::select::Case<>); }; template inline void Printer::printAttribute(std::string_view name, const Type& value) const { assert(!name.empty()); printIndent(); m_stream << name << " = "; PrintHelper::print(m_stream, value, -m_levelPlusOne, m_spaces); } template inline void Printer::printAttributeAsHex(std::string_view name, const Type& value) const { static_assert(std::is_integral::value, "type should be integral"); assert(!name.empty()); printIndent(); m_stream << name << " = "; { FormatFlagsGuard guard(m_stream); m_stream << std::hex << value; } if (m_spaces >= 0) { m_stream << '\n'; } } template inline void Printer::printAttribute(std::string_view name, const InputIt& begin, const InputIt& end) const { assert(!name.empty()); printIndent(); m_stream << name << " = "; PrintHelper::print(m_stream, begin, end, -m_levelPlusOne, m_spaces); } template inline void Printer::printValue(const Type& value) const { printIndent(); PrintHelper::print(m_stream, value, -m_levelPlusOne, m_spaces); } template inline void Printer::printValue(const InputIt& begin, const InputIt& end) const { printIndent(); PrintHelper::print(m_stream, begin, end, -m_levelPlusOne, m_spaces); } template inline void Printer::printForeignAttribute( std::string_view name, const Type& value, const PrintFunction& printFunction) const { assert(!name.empty()); printIndent(); m_stream << name << " = "; printFunction(m_stream, value, -m_levelPlusOne, m_spaces); } template inline void Printer::printForeignValue(const Type& value, const PrintFunction& printFunction) const { printIndent(); printFunction(m_stream, value, -m_levelPlusOne, m_spaces); } template inline void Printer::printOrNull(std::string_view name, const Type& address) const { assert(!name.empty()); printIndent(); m_stream << name << " = "; if (address == nullptr) { m_stream << "null"; if (m_spaces >= 0) { m_stream << '\n'; } } else { PrintHelper::print(m_stream, *address, -m_levelPlusOne, m_spaces); } } template inline void Printer::printOrNull(const Type& address) const { printIndent(); if (address == nullptr) { m_stream << "null"; if (m_spaces >= 0) { m_stream << '\n'; } } else { PrintHelper::print(m_stream, *address, -m_levelPlusOne, m_spaces); } } template <> inline void Printer::printOrNull(std::string_view name, const void* const& address) const { assert(!name.empty()); printIndent(); m_stream << name << " = "; const void* temp = address; PrintHelper::print(m_stream, temp, -m_levelPlusOne, m_spaces); } template <> inline void Printer::printOrNull(std::string_view name, void* const& address) const { const void* const& temp = address; printOrNull(name, temp); } template <> inline void Printer::printOrNull(const void* const& address) const { printIndent(); const void* temp = address; PrintHelper::print(m_stream, temp, -m_levelPlusOne, m_spaces); } template <> inline void Printer::printOrNull(void* const& address) const { const void* const& temp = address; printOrNull(temp); } // Print Helper methods template inline void PrintHelper::print( std::ostream& stream, const InputIt& begin, const InputIt& end, int level, int spaces) { Printer printer(stream, level, spaces); std::for_each(begin, end, [&](const auto& x) { printer.printValue(x); }); } template inline void PrintHelper::printType( std::ostream& stream, Type value, int, int spaces, traits::select::Case) { stream << value; if (spaces >= 0) { stream << '\n'; } } template inline void PrintHelper::printType( std::ostream& stream, Type value, int, int spaces, traits::select::Case) { printType(stream, value, 0, spaces, traits::select::Case()); } template inline void PrintHelper::printType( std::ostream& stream, Type value, int level, int spaces, traits::select::Case) { PrintHelper::print(stream, reinterpret_cast(value), level, spaces); } template inline void PrintHelper::printType( std::ostream& stream, const Type* value, int level, int spaces, traits::select::Case) { printType( stream, static_cast(value), level, -1, traits::select::Case()); if (value == nullptr) { if (spaces >= 0) { stream << '\n'; } } else { stream << ' '; PrintHelper::print(stream, *value, level, spaces); } } template inline void PrintHelper::printType( std::ostream& stream, const Type* value, int level, int spaces, traits::select::Case) { printType(stream, value, level, spaces, traits::select::Case()); } inline void PrintHelper::printType( std::ostream& stream, const std::string& value, int level, int spaces, traits::select::Case) { printType(stream, value.c_str(), level, spaces, traits::select::Case()); } template inline void PrintHelper::printType( std::ostream& stream, const Type& value, int level, int spaces, traits::select::Case) { print(stream, value.begin(), value.end(), level, spaces); } template inline void PrintHelper::printType( std::ostream& stream, const std::pair& value, int level, int spaces, traits::select::Case<>) { Printer print(stream, level, spaces); print.printValue(value.first); print.printValue(value.second); } template inline void PrintHelper::printType( std::ostream& stream, const std::tuple& value, int level, int spaces, traits::select::Case<>) { Printer print(stream, level, spaces); traits::for_each_in_tuple(value, [&](const auto& x) { print.printValue(x); }); } template inline void PrintHelper::printType( std::ostream& stream, const Type& value, int level, int spaces, traits::select::Case<>) { value.print(stream, level, spaces); } template inline void PrintHelper::print(std::ostream& stream, const Type& value, int level, int spaces) { using Selection = traits::select::Select< Type, std::is_fundamental, std::is_enum, std::is_function, std::is_pointer, std::is_array, traits::is_container>; PrintHelper::printType(stream, value, level, spaces, Selection()); } } // namespace llarp