lokinet/llarp/util/printer.hpp
2019-03-18 16:12:42 -05:00

513 lines
14 KiB
C++

#ifndef LLARP_PRINTER_HPP
#define LLARP_PRINTER_HPP
#include <util/string_view.hpp>
#include <util/traits.hpp>
#include <functional>
#include <iostream>
#include <assert.h>
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 < typename Type >
using PrintFunction =
std::function< std::ostream&(std::ostream&, const Type&, int, int) >;
/// 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 `llarp::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 < typename Type >
void
printAttribute(string_view name, const Type& value) const;
template < typename Type >
void
printAttributeAsHex(string_view name, const Type& value) const;
template < typename InputIt >
void
printAttribute(string_view name, const InputIt& begin,
const InputIt& end) const;
template < typename Type >
void
printValue(const Type& value) const;
template < typename InputIt >
void
printValue(const InputIt& begin, const InputIt& end) const;
template < typename Type >
void
printForeignAttribute(string_view name, const Type& value,
const PrintFunction< Type >& printFunction) const;
template < typename Type >
void
printForeignValue(const Type& value,
const PrintFunction< Type >& printFunction) const;
void
printHexAddr(string_view name, const void* address) const;
void
printHexAddr(const void* address) const;
template < class Type >
void
printOrNull(string_view name, const Type& address) const;
template < class Type >
void
printOrNull(const Type& address) const;
private:
void
printIndent() const;
};
/// helper struct
struct PrintHelper
{
template < typename Type >
static void
print(std::ostream& stream, const Type& value, int level, int spaces);
template < typename InputIt >
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< std::is_fundamental >);
static void
printType(std::ostream& stream, bool value, int level, int spaces,
traits::select::Case< std::is_fundamental >);
template < typename Type >
static void
printType(std::ostream& stream, Type value, int level, int spaces,
traits::select::Case< std::is_fundamental >);
template < typename Type >
static void
printType(std::ostream& stream, Type value, int level, int spaces,
traits::select::Case< std::is_enum >);
// Function types
template < typename Type >
static void
printType(std::ostream& stream, Type value, int level, int spaces,
traits::select::Case< std::is_function >);
// Pointer types
static void
printType(std::ostream& stream, const char* value, int level, int spaces,
traits::select::Case< std::is_pointer >);
static void
printType(std::ostream& stream, const void* value, int level, int spaces,
traits::select::Case< std::is_pointer >);
template < typename Type >
static void
printType(std::ostream& stream, const Type* value, int level, int spaces,
traits::select::Case< std::is_pointer >);
template < typename Type >
static void
printType(std::ostream& stream, const Type* value, int level, int spaces,
traits::select::Case< std::is_array >);
// Container types
static void
printType(std::ostream& stream, const std::string& value, int level,
int spaces, traits::select::Case< traits::is_container >);
static void
printType(std::ostream& stream, const string_view& value, int level,
int spaces, traits::select::Case< traits::is_container >);
template < typename Type >
static void
printType(std::ostream& stream, const Type& value, int level, int spaces,
traits::select::Case< traits::is_container >);
// Utility types
template < typename Type1, typename Type2 >
static void
printType(std::ostream& stream, const std::pair< Type1, Type2 >& value,
int level, int spaces, traits::select::Case<>);
template < typename... Types >
static void
printType(std::ostream& stream, const std::tuple< Types... >& value,
int level, int spaces, traits::select::Case<>);
// Default type
template < typename Type >
static void
printType(std::ostream& stream, const Type& value, int level, int spaces,
traits::select::Case<>);
};
template < typename Type >
inline void
Printer::printAttribute(string_view name, const Type& value) const
{
assert(!name.empty());
printIndent();
m_stream << name << " = ";
PrintHelper::print(m_stream, value, -m_levelPlusOne, m_spaces);
}
template < typename Type >
inline void
Printer::printAttributeAsHex(string_view name, const Type& value) const
{
static_assert(std::is_integral< Type >::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 < typename InputIt >
inline void
Printer::printAttribute(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 < typename Type >
inline void
Printer::printValue(const Type& value) const
{
printIndent();
PrintHelper::print(m_stream, value, -m_levelPlusOne, m_spaces);
}
template < typename InputIt >
inline void
Printer::printValue(const InputIt& begin, const InputIt& end) const
{
printIndent();
PrintHelper::print(m_stream, begin, end, -m_levelPlusOne, m_spaces);
}
template < typename Type >
inline void
Printer::printForeignAttribute(
string_view name, const Type& value,
const PrintFunction< Type >& printFunction) const
{
assert(!name.empty());
printIndent();
m_stream << name << " = ";
printFunction(m_stream, value, -m_levelPlusOne, m_spaces);
}
template < typename Type >
inline void
Printer::printForeignValue(const Type& value,
const PrintFunction< Type >& printFunction) const
{
printIndent();
printFunction(m_stream, value, -m_levelPlusOne, m_spaces);
}
template < typename Type >
inline void
Printer::printOrNull(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 < typename Type >
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< const void* >(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< void* >(string_view name, void* const& address) const
{
const void* const& temp = address;
printOrNull(name, temp);
}
template <>
inline void
Printer::printOrNull< const void* >(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* >(void* const& address) const
{
const void* const& temp = address;
printOrNull(temp);
}
// Print Helper methods
template < typename InputIt >
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 < typename Type >
inline void
PrintHelper::printType(std::ostream& stream, Type value, int, int spaces,
traits::select::Case< std::is_fundamental >)
{
stream << value;
if(spaces >= 0)
{
stream << '\n';
}
}
template < typename Type >
inline void
PrintHelper::printType(std::ostream& stream, Type value, int, int spaces,
traits::select::Case< std::is_enum >)
{
printType(stream, value, 0, spaces,
traits::select::Case< std::is_fundamental >());
}
template < typename Type >
inline void
PrintHelper::printType(std::ostream& stream, Type value, int level,
int spaces, traits::select::Case< std::is_function >)
{
PrintHelper::print(stream, reinterpret_cast< const void* >(value), level,
spaces);
}
template < typename Type >
inline void
PrintHelper::printType(std::ostream& stream, const Type* value, int level,
int spaces, traits::select::Case< std::is_pointer >)
{
printType(stream, static_cast< const void* >(value), level, -1,
traits::select::Case< std::is_pointer >());
if(value == nullptr)
{
if(spaces >= 0)
{
stream << '\n';
}
}
else
{
stream << ' ';
PrintHelper::print(stream, *value, level, spaces);
}
}
template < typename Type >
inline void
PrintHelper::printType(std::ostream& stream, const Type* value, int level,
int spaces, traits::select::Case< std::is_array >)
{
printType(stream, value, level, spaces,
traits::select::Case< std::is_pointer >());
}
inline void
PrintHelper::printType(std::ostream& stream, const std::string& value,
int level, int spaces,
traits::select::Case< traits::is_container >)
{
printType(stream, value.c_str(), level, spaces,
traits::select::Case< std::is_pointer >());
}
template < typename Type >
inline void
PrintHelper::printType(std::ostream& stream, const Type& value, int level,
int spaces,
traits::select::Case< traits::is_container >)
{
print(stream, value.begin(), value.end(), level, spaces);
}
template < typename Type1, typename Type2 >
inline void
PrintHelper::printType(std::ostream& stream,
const std::pair< Type1, Type2 >& value, int level,
int spaces, traits::select::Case<>)
{
Printer print(stream, level, spaces);
print.printValue(value.first);
print.printValue(value.second);
}
template < typename... Types >
inline void
PrintHelper::printType(std::ostream& stream,
const std::tuple< Types... >& 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 < typename Type >
inline void
PrintHelper::printType(std::ostream& stream, const Type& value, int level,
int spaces, traits::select::Case<>)
{
value.print(stream, level, spaces);
}
template < typename Type >
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
#endif