mirror of https://github.com/oxen-io/lokinet
Create Printer - A general-purpose, stateful printer class
This commit is contained in:
parent
95a5c386fe
commit
e6e19369e9
|
@ -19,6 +19,7 @@ set(LIB_UTIL_SRC
|
|||
util/mem.cpp
|
||||
util/queue_manager.cpp
|
||||
util/queue.cpp
|
||||
util/printer.cpp
|
||||
util/status.cpp
|
||||
util/str.cpp
|
||||
util/string_view.cpp
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
#include <util/printer.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
static void
|
||||
putSpaces(std::ostream& stream, size_t count)
|
||||
{
|
||||
// chunk n write
|
||||
static const char spaces[] = " ";
|
||||
static constexpr size_t size = sizeof(spaces) - 1;
|
||||
|
||||
while(size < count)
|
||||
{
|
||||
stream.write(spaces, size);
|
||||
count -= size;
|
||||
}
|
||||
|
||||
if(count > 0)
|
||||
{
|
||||
stream.write(spaces, count);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Printer::Printer(std::ostream& stream, int level, int spacesPerLevel)
|
||||
: m_stream(stream)
|
||||
, m_level(level < 0 ? -level : level)
|
||||
, m_levelPlusOne(m_level + 1)
|
||||
, m_suppressIndent(level < 0)
|
||||
, m_spaces(spacesPerLevel)
|
||||
{
|
||||
if(!m_suppressIndent)
|
||||
{
|
||||
const int absSpaces = m_spaces < 0 ? -m_spaces : m_spaces;
|
||||
putSpaces(m_stream, absSpaces * m_level);
|
||||
}
|
||||
|
||||
m_stream << '[';
|
||||
if(m_spaces >= 0)
|
||||
{
|
||||
m_stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
Printer::~Printer()
|
||||
{
|
||||
putSpaces(m_stream, m_spaces < 0 ? 1 : m_spaces * m_level);
|
||||
m_stream << ']';
|
||||
}
|
||||
|
||||
void
|
||||
Printer::printIndent() const
|
||||
{
|
||||
putSpaces(m_stream, m_spaces < 0 ? 1 : m_spaces * m_levelPlusOne);
|
||||
}
|
||||
|
||||
void
|
||||
Printer::printHexAddr(string_view name, const void* address) const
|
||||
{
|
||||
printIndent();
|
||||
m_stream << name << " = ";
|
||||
|
||||
PrintHelper::print(m_stream, address, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
void
|
||||
Printer::printHexAddr(const void* address) const
|
||||
{
|
||||
printIndent();
|
||||
PrintHelper::print(m_stream, address, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, char value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< std::is_fundamental >)
|
||||
{
|
||||
if(std::isprint(static_cast< unsigned char >(value)))
|
||||
{
|
||||
stream << "'" << value << "'";
|
||||
}
|
||||
else
|
||||
{
|
||||
#define PRINT_CONTROL_CHAR(x) \
|
||||
case x: \
|
||||
stream << #x; \
|
||||
break;
|
||||
|
||||
switch(value)
|
||||
{
|
||||
PRINT_CONTROL_CHAR('\n');
|
||||
PRINT_CONTROL_CHAR('\t');
|
||||
PRINT_CONTROL_CHAR('\0');
|
||||
default:
|
||||
{
|
||||
// Print as hex
|
||||
FormatFlagsGuard guard(stream);
|
||||
stream << std::hex << std::showbase
|
||||
<< static_cast< std::uintptr_t >(
|
||||
static_cast< unsigned char >(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, bool value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< std::is_fundamental >)
|
||||
{
|
||||
{
|
||||
FormatFlagsGuard guard(stream);
|
||||
stream << std::boolalpha << value;
|
||||
}
|
||||
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, const char* value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< std::is_pointer >)
|
||||
{
|
||||
if(value == nullptr)
|
||||
{
|
||||
stream << "null";
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << '"' << value << '"';
|
||||
}
|
||||
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, const void* value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< std::is_pointer >)
|
||||
{
|
||||
if(value == nullptr)
|
||||
{
|
||||
stream << "null";
|
||||
}
|
||||
else
|
||||
{
|
||||
FormatFlagsGuard guard(stream);
|
||||
stream << std::hex << std::showbase
|
||||
<< reinterpret_cast< std::uintptr_t >(value);
|
||||
}
|
||||
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, const string_view& value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< traits::is_container >)
|
||||
{
|
||||
stream << '"' << value << '"';
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace llarp
|
|
@ -0,0 +1,511 @@
|
|||
#ifndef LLARP_PRINTER_HPP
|
||||
#define LLARP_PRINTER_HPP
|
||||
|
||||
#include <util/string_view.hpp>
|
||||
#include <util/traits.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
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
|
|
@ -2,6 +2,7 @@
|
|||
#define LLARP_TRAITS_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -93,15 +94,15 @@ namespace llarp
|
|||
|
||||
/// implementation helper
|
||||
template < typename T,
|
||||
template < typename > class Trait1,
|
||||
template < typename > class Trait2,
|
||||
template < typename > class Trait3,
|
||||
template < typename > class Trait4,
|
||||
template < typename > class Trait5,
|
||||
template < typename > class Trait6,
|
||||
template < typename > class Trait7,
|
||||
template < typename > class Trait8,
|
||||
template < typename > class Trait9 >
|
||||
template < typename... > class Trait1,
|
||||
template < typename... > class Trait2,
|
||||
template < typename... > class Trait3,
|
||||
template < typename... > class Trait4,
|
||||
template < typename... > class Trait5,
|
||||
template < typename... > class Trait6,
|
||||
template < typename... > class Trait7,
|
||||
template < typename... > class Trait8,
|
||||
template < typename... > class Trait9 >
|
||||
struct SelectHelper {
|
||||
enum {
|
||||
Selector = (
|
||||
|
@ -133,15 +134,15 @@ namespace llarp
|
|||
};
|
||||
|
||||
template< typename Type,
|
||||
template < typename > class Trait1,
|
||||
template < typename > class Trait2 = False,
|
||||
template < typename > class Trait3 = False,
|
||||
template < typename > class Trait4 = False,
|
||||
template < typename > class Trait5 = False,
|
||||
template < typename > class Trait6 = False,
|
||||
template < typename > class Trait7 = False,
|
||||
template < typename > class Trait8 = False,
|
||||
template < typename > class Trait9 = False >
|
||||
template < typename... > class Trait1,
|
||||
template < typename... > class Trait2 = False,
|
||||
template < typename... > class Trait3 = False,
|
||||
template < typename... > class Trait4 = False,
|
||||
template < typename... > class Trait5 = False,
|
||||
template < typename... > class Trait6 = False,
|
||||
template < typename... > class Trait7 = False,
|
||||
template < typename... > class Trait8 = False,
|
||||
template < typename... > class Trait9 = False >
|
||||
struct Select : public SelectHelper< Type,
|
||||
Trait1,
|
||||
Trait2,
|
||||
|
@ -171,7 +172,26 @@ namespace llarp
|
|||
|
||||
// clang-format on
|
||||
} // namespace select
|
||||
} // namespace traits
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template < typename T, typename F, size_t... Is >
|
||||
void
|
||||
for_each(T&& t, F f, std::index_sequence< Is... >)
|
||||
{
|
||||
auto l = {(f(std::get< Is >(t)), 0)...};
|
||||
(void)l;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template < typename... Ts, typename F >
|
||||
void
|
||||
for_each_in_tuple(std::tuple< Ts... > const& t, F f)
|
||||
{
|
||||
detail::for_each(t, f, std::make_index_sequence< sizeof...(Ts) >());
|
||||
}
|
||||
|
||||
} // namespace traits
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
||||
|
|
|
@ -34,6 +34,7 @@ list(APPEND TEST_SRC
|
|||
util/test_llarp_util_bits.cpp
|
||||
util/test_llarp_util_encode.cpp
|
||||
util/test_llarp_util_ini.cpp
|
||||
util/test_llarp_util_printer.cpp
|
||||
util/test_llarp_util_queue_manager.cpp
|
||||
util/test_llarp_util_queue.cpp
|
||||
util/test_llarp_util_thread_pool.cpp
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
#include <util/printer.hpp>
|
||||
|
||||
#include <absl/types/variant.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using namespace llarp;
|
||||
using namespace ::testing;
|
||||
|
||||
struct PrintableType
|
||||
{
|
||||
std::ostream &
|
||||
print(std::ostream &stream, int level, int spaces) const
|
||||
{
|
||||
stream << "PrintableType " << level << " " << spaces;
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
using SingleVariant =
|
||||
absl::variant< char, bool, short, int, unsigned int, const void *,
|
||||
const char *, std::string, const int *,
|
||||
std::pair< int, std::string >,
|
||||
std::tuple< int, std::string, int >,
|
||||
std::map< std::string, char >, PrintableType >;
|
||||
|
||||
using SingleType = std::pair< SingleVariant, Matcher< std::string > >;
|
||||
|
||||
class SingleValueTest : public ::testing::TestWithParam< SingleType >
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(SingleValueTest, value)
|
||||
{
|
||||
SingleType d = GetParam();
|
||||
std::ostringstream stream;
|
||||
{
|
||||
Printer printer(stream, -1, -1);
|
||||
absl::visit([&](const auto &x) { printer.printValue(x); }, d.first);
|
||||
}
|
||||
ASSERT_THAT(stream.str(), d.second);
|
||||
}
|
||||
|
||||
static const char PTR_TYPE[] = "abacus";
|
||||
static const int INT_VAL = 100;
|
||||
|
||||
// clang-format off
|
||||
static const SingleType singleType[] = {
|
||||
{char('a'), StrEq("[ 'a' ]")},
|
||||
{bool(true), StrEq("[ true ]")},
|
||||
{bool(false), StrEq("[ false ]")},
|
||||
{short(123), StrEq("[ 123 ]")},
|
||||
{int(INT_MAX - 1), StrEq("[ 2147483646 ]")},
|
||||
{static_cast< unsigned int >(std::numeric_limits< int >::max()) + 1, StrEq("[ 2147483648 ]")},
|
||||
{static_cast< const void * >(PTR_TYPE), AllOf(StartsWith("[ 0x"), EndsWith(" ]"))},
|
||||
{static_cast< const char * >(PTR_TYPE), StrEq("[ \"abacus\" ]")},
|
||||
{std::string("abacus"), StrEq("[ \"abacus\" ]")},
|
||||
{static_cast< const int * >(&INT_VAL), AllOf(StartsWith("[ 0x"), EndsWith(" ]"))},
|
||||
{std::pair< int, std::string >(100, "abacus"), StrEq("[ [ 100 \"abacus\" ] ]")},
|
||||
{std::tuple< int, std::string, int >(100, "abacus", 123), StrEq("[ [ 100 \"abacus\" 123 ] ]")},
|
||||
{std::map< std::string, char >{{"one", 'a'}, {"two", 'b'}, {"three", 'c'}}, StrEq("[ [ [ \"one\" \'a\' ] [ \"three\" \'c\' ] [ \"two\" 'b' ] ] ]")},
|
||||
{PrintableType(), StrEq("[ PrintableType -2 -1 ]")},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Printer, SingleValueTest,
|
||||
::testing::ValuesIn(singleType), );
|
||||
|
||||
using SingleAttributeType =
|
||||
std::tuple< std::string, SingleVariant, Matcher< std::string > >;
|
||||
|
||||
class SingleAttributeTest
|
||||
: public ::testing::TestWithParam< SingleAttributeType >
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(SingleAttributeTest, value)
|
||||
{
|
||||
SingleAttributeType d = GetParam();
|
||||
std::ostringstream stream;
|
||||
{
|
||||
Printer printer(stream, -1, -1);
|
||||
absl::visit(
|
||||
[&](const auto &x) { printer.printAttribute(std::get< 0 >(d), x); },
|
||||
std::get< 1 >(d));
|
||||
}
|
||||
ASSERT_THAT(stream.str(), std::get< 2 >(d));
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const SingleAttributeType singleAttributeType[] = {
|
||||
{"our_value", char('a'), StrEq("[ our_value = 'a' ]")},
|
||||
{"our_value", bool(true), StrEq("[ our_value = true ]")},
|
||||
{"our_value", bool(false), StrEq("[ our_value = false ]")},
|
||||
{"our_value", short(123), StrEq("[ our_value = 123 ]")},
|
||||
{"our_value", int(INT_MAX - 1), StrEq("[ our_value = 2147483646 ]")},
|
||||
{"our_value", static_cast< unsigned int >(std::numeric_limits< int >::max()) + 1, StrEq("[ our_value = 2147483648 ]")},
|
||||
{"our_value", static_cast< const void * >(PTR_TYPE), AllOf(StartsWith("[ our_value = 0x"), EndsWith(" ]"))},
|
||||
{"our_value", static_cast< const char * >(PTR_TYPE), StrEq("[ our_value = \"abacus\" ]")},
|
||||
{"our_value", std::string("abacus"), StrEq("[ our_value = \"abacus\" ]")},
|
||||
{"our_value", static_cast< const int * >(&INT_VAL), AllOf(StartsWith("[ our_value = 0x"), EndsWith(" ]"))},
|
||||
{"our_value", std::pair< int, std::string >(100, "abacus"), StrEq("[ our_value = [ 100 \"abacus\" ] ]")},
|
||||
{"our_value", std::tuple< int, std::string, int >(100, "abacus", 123), StrEq("[ our_value = [ 100 \"abacus\" 123 ] ]")},
|
||||
{"our_value", std::map< std::string, char >{{"one", 'a'}, {"two", 'b'}, {"three", 'c'}}, StrEq("[ our_value = [ [ \"one\" \'a\' ] [ \"three\" \'c\' ] [ \"two\" 'b' ] ] ]")},
|
||||
{"our_value", PrintableType(), StrEq("[ our_value = PrintableType -2 -1 ]")},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Printer, SingleAttributeTest,
|
||||
::testing::ValuesIn(singleAttributeType), );
|
||||
|
||||
using ManyAttributes =
|
||||
std::pair< std::vector< std::pair< std::string, SingleVariant > >,
|
||||
Matcher< std::string > >;
|
||||
|
||||
class ManyAttributesTest : public ::testing::TestWithParam< ManyAttributes >
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(ManyAttributesTest, value)
|
||||
{
|
||||
ManyAttributes d = GetParam();
|
||||
std::ostringstream stream;
|
||||
{
|
||||
Printer printer(stream, -1, -1);
|
||||
std::for_each(d.first.begin(), d.first.end(), [&](const auto &y) {
|
||||
std::string n = y.first;
|
||||
const auto &v = y.second;
|
||||
absl::visit([&](const auto &x) { printer.printAttribute(n, x); }, v);
|
||||
});
|
||||
}
|
||||
ASSERT_THAT(stream.str(), d.second);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const ManyAttributes manyAttributes[] = {
|
||||
{{{"val", 1}, {"v2", 2}, {"v3", 3}, {"str", std::string("xxx")}}, StrEq("[ val = 1 v2 = 2 v3 = 3 str = \"xxx\" ]")},
|
||||
{{{"str", std::string("xxx")}}, StrEq("[ str = \"xxx\" ]")}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Printer, ManyAttributesTest,
|
||||
::testing::ValuesIn(manyAttributes), );
|
Loading…
Reference in New Issue