mirror of https://github.com/oxen-io/lokinet
Remote util::Printer and related cruft
parent
2f9e182b20
commit
eec8244a6c
@ -1,209 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace traits
|
||||
{
|
||||
/// Represents the empty type
|
||||
struct Bottom
|
||||
{};
|
||||
|
||||
/// Int tag
|
||||
template <size_t N>
|
||||
struct Tag
|
||||
{
|
||||
char arr[N + 1];
|
||||
};
|
||||
|
||||
/// Type trait representing whether a type is pointer-like
|
||||
template <typename T, typename _ = void>
|
||||
struct is_pointy : public std::false_type
|
||||
{};
|
||||
|
||||
// We take the following things:
|
||||
// - has element_type typedef
|
||||
// - has dereference operator
|
||||
// - has arrow operator
|
||||
template <typename T>
|
||||
struct is_pointy<T, std::conditional_t<false, std::void_t<decltype(*std::declval<T>())>, void>>
|
||||
: public std::true_type
|
||||
{};
|
||||
|
||||
/// Type trait representing whether a type is an STL-style container
|
||||
template <typename T, typename _ = void>
|
||||
struct is_container : public std::false_type
|
||||
{};
|
||||
|
||||
// We take that the container has begin, end and size methods to be a
|
||||
// container.
|
||||
// clang-format off
|
||||
template < typename T >
|
||||
struct is_container<
|
||||
T,
|
||||
std::conditional_t<
|
||||
false,
|
||||
std::void_t< typename T::value_type,
|
||||
typename T::size_type,
|
||||
typename T::iterator,
|
||||
typename T::const_iterator,
|
||||
decltype(std::declval<T>().size()),
|
||||
decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end()),
|
||||
decltype(std::declval<T>().cbegin()),
|
||||
decltype(std::declval<T>().cend()) >,
|
||||
void > > : public std::true_type
|
||||
{
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
namespace Switch
|
||||
{
|
||||
template <size_t Selector, typename... Types>
|
||||
struct Switch
|
||||
{
|
||||
using Type = Bottom;
|
||||
};
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct Switch<0u, T, Types...>
|
||||
{
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <size_t Selector, typename Tn, typename... Types>
|
||||
struct Switch<Selector, Tn, Types...>
|
||||
{
|
||||
using Type = typename Switch<Selector - 1, Types...>::Type;
|
||||
};
|
||||
|
||||
} // namespace Switch
|
||||
|
||||
namespace select
|
||||
{
|
||||
/// This provides a way to do a compile-type dispatch based on type traits
|
||||
|
||||
/// meta function which always returns false
|
||||
template <typename>
|
||||
class False : public std::false_type
|
||||
{};
|
||||
|
||||
/// a case in the selection
|
||||
template <template <typename...> class Trait = False>
|
||||
class Case
|
||||
{
|
||||
public:
|
||||
template <typename Type>
|
||||
struct Selector : public Trait<Type>::type
|
||||
{};
|
||||
|
||||
using Type = Case;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
/// 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 >
|
||||
struct SelectHelper {
|
||||
enum {
|
||||
Selector = (
|
||||
Trait1<T>::value ? 1 :
|
||||
Trait2<T>::value ? 2 :
|
||||
Trait3<T>::value ? 3 :
|
||||
Trait4<T>::value ? 4 :
|
||||
Trait5<T>::value ? 5 :
|
||||
Trait6<T>::value ? 6 :
|
||||
Trait7<T>::value ? 7 :
|
||||
Trait8<T>::value ? 8 :
|
||||
Trait9<T>::value ? 9 : 0
|
||||
)
|
||||
};
|
||||
|
||||
using Type = typename Switch::Switch<
|
||||
Selector,
|
||||
Case<>,
|
||||
Case<Trait1>,
|
||||
Case<Trait2>,
|
||||
Case<Trait3>,
|
||||
Case<Trait4>,
|
||||
Case<Trait5>,
|
||||
Case<Trait6>,
|
||||
Case<Trait7>,
|
||||
Case<Trait8>,
|
||||
Case<Trait9>
|
||||
>::Type;
|
||||
};
|
||||
|
||||
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 >
|
||||
struct Select : public SelectHelper< Type,
|
||||
Trait1,
|
||||
Trait2,
|
||||
Trait3,
|
||||
Trait4,
|
||||
Trait5,
|
||||
Trait6,
|
||||
Trait7,
|
||||
Trait8,
|
||||
Trait9 >::Type
|
||||
{
|
||||
enum {
|
||||
Selector = SelectHelper< Type,
|
||||
Trait1,
|
||||
Trait2,
|
||||
Trait3,
|
||||
Trait4,
|
||||
Trait5,
|
||||
Trait6,
|
||||
Trait7,
|
||||
Trait8,
|
||||
Trait9 >::Selector
|
||||
};
|
||||
|
||||
using SelectorType = std::integral_constant<int, Selector>;
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace select
|
||||
|
||||
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
|
@ -1,197 +0,0 @@
|
||||
#include "printer.hpp"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
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(std::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 std::string_view& value,
|
||||
int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case<traits::is_container>)
|
||||
{
|
||||
stream << '"' << value << '"';
|
||||
if (spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace llarp
|
@ -1,583 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <llarp/util/meta/traits.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
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 `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 <typename Type>
|
||||
void
|
||||
printAttribute(std::string_view name, const Type& value) const;
|
||||
|
||||
template <typename Type>
|
||||
void
|
||||
printAttributeAsHex(std::string_view name, const Type& value) const;
|
||||
|
||||
template <typename InputIt>
|
||||
void
|
||||
printAttribute(std::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(
|
||||
std::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(std::string_view name, const void* address) const;
|
||||
void
|
||||
printHexAddr(const void* address) const;
|
||||
|
||||
template <class Type>
|
||||
void
|
||||
printOrNull(std::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 std::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(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 <typename Type>
|
||||
inline void
|
||||
Printer::printAttributeAsHex(std::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(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 <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(
|
||||
std::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(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 <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*>(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<void*>(std::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
|
@ -1,139 +0,0 @@
|
||||
#include <util/meta/traits.hpp>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
using namespace llarp;
|
||||
|
||||
TEST_CASE("traits::Bottom smoke test")
|
||||
{
|
||||
traits::Bottom bottom;
|
||||
(void)bottom;
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
using ContainerTypes = std::tuple<
|
||||
std::tuple< std::vector< int >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::vector< std::string >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::list< std::string >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::string, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::shared_ptr<std::string>, std::integral_constant< bool, false > >,
|
||||
std::tuple< std::tuple<std::string>, std::integral_constant< bool, false > >,
|
||||
std::tuple< int, std::integral_constant< bool, false > >
|
||||
>;
|
||||
// clang-format on
|
||||
|
||||
TEMPLATE_LIST_TEST_CASE("is_container smoke test", "", ContainerTypes)
|
||||
{
|
||||
bool expected = std::tuple_element_t<1, TestType>::value;
|
||||
bool result = traits::is_container<std::tuple_element_t<0, TestType>>::value;
|
||||
REQUIRE(expected == result);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
struct A { };
|
||||
struct B { };
|
||||
struct C { };
|
||||
struct D { };
|
||||
struct E { };
|
||||
struct F { };
|
||||
struct G { };
|
||||
struct H { };
|
||||
struct I { };
|
||||
struct J { };
|
||||
|
||||
char f(A) { return 'A'; }
|
||||
char f(B) { return 'B'; }
|
||||
char f(C) { return 'C'; }
|
||||
char f(D) { return 'D'; }
|
||||
char f(E) { return 'E'; }
|
||||
char f(F) { return 'F'; }
|
||||
char f(G) { return 'G'; }
|
||||
char f(H) { return 'H'; }
|
||||
char f(I) { return 'I'; }
|
||||
char f(J) { return 'J'; }
|
||||
char f(traits::Bottom) { return '0'; }
|
||||
// clang-format on
|
||||
|
||||
// clang-format off
|
||||
using namespace traits::Switch;
|
||||
using SwitchTypes = std::tuple<
|
||||
std::tuple<std::integral_constant<char, 'A'>, Switch< 0, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'B'>, Switch< 1, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'C'>, Switch< 2, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'D'>, Switch< 3, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'E'>, Switch< 4, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'F'>, Switch< 5, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'G'>, Switch< 6, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'H'>, Switch< 7, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'I'>, Switch< 8, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'J'>, Switch< 9, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'J'>, Switch< 9, C, C, C, C, C, C, C, C, C, J > >,
|
||||
std::tuple<std::integral_constant<char, 'C'>, Switch< 6, C, C, C, C, C, C, C, C, C, J > >,
|
||||
std::tuple<std::integral_constant<char, '0'>, Switch< 10, A, B, C, D, E, F, G, H, I, J > >
|
||||
>;
|
||||
// clang-format off
|
||||
|
||||
TEMPLATE_LIST_TEST_CASE("Switch smoke test", "", SwitchTypes)
|
||||
{
|
||||
char expected = std::tuple_element_t<0, TestType>::value;
|
||||
using InputType = typename std::tuple_element_t<1, TestType>::Type;
|
||||
char result = f(InputType());
|
||||
REQUIRE(expected == result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
using is_bool = std::is_same<T, bool>;
|
||||
template<typename T>
|
||||
using is_char = std::is_same<T, char>;
|
||||
template<typename T>
|
||||
using is_string = std::is_same<T, std::string>;
|
||||
|
||||
char dispatch(traits::select::Case<>) { return '0'; }
|
||||
char dispatch(traits::select::Case<is_bool>) { return 'b'; }
|
||||
char dispatch(traits::select::Case<is_char>) { return 'c'; }
|
||||
char dispatch(traits::select::Case<is_string>) { return 's'; }
|
||||
|
||||
template < typename Type >
|
||||
char
|
||||
selectCase()
|
||||
{
|
||||
using Selection = traits::select::Select<Type, is_bool, is_char, is_string >;
|
||||
|
||||
return dispatch(Selection());
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
using SelectTypes = std::tuple<
|
||||
std::tuple<std::integral_constant<char, '0'>, double >,
|
||||
std::tuple<std::integral_constant<char, 'b'>, bool >,
|
||||
std::tuple<std::integral_constant<char, 'c'>, char >,
|
||||
std::tuple<std::integral_constant<char, 's'>, std::string >
|
||||
>;
|
||||
// clang-format on
|
||||
|
||||
TEMPLATE_LIST_TEST_CASE("selectCase smoke test", "", SelectTypes)
|
||||
{
|
||||
char expected = std::tuple_element_t<0, TestType>::value;
|
||||
char result = selectCase<std::tuple_element_t<1, TestType>>();
|
||||
REQUIRE(expected == result);
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// clang-format off
|
||||
using PointerTypes = std::tuple<
|
||||
std::tuple< int *, std::true_type >,
|
||||
std::tuple< int, std::integral_constant< bool, false > >,
|
||||
std::tuple< std::shared_ptr<int>, std::true_type >,
|
||||
std::tuple< std::unique_ptr<int>, std::true_type >
|
||||
>;
|
||||
// clang-format on
|
||||
|
||||
TEMPLATE_LIST_TEST_CASE("is_pointy smoke test", "", PointerTypes)
|
||||
{
|
||||
bool expected = std::tuple_element_t<1, TestType>::value;
|
||||
bool result = traits::is_pointy<std::tuple_element_t<0, TestType>>::value;
|
||||
REQUIRE(expected == result);
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
#include <util/printer.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
using namespace llarp;
|
||||
|
||||
struct PrintableType
|
||||
{
|
||||
std::ostream &
|
||||
print(std::ostream &stream, int level, int spaces) const
|
||||
{
|
||||
stream << "PrintableType " << level << " " << spaces;
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::string print(const T &x) {
|
||||
std::ostringstream os;
|
||||
{
|
||||
Printer printer(os, -1, -1);
|
||||
printer.printValue(x);
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
TEST_CASE("printable types", "[printer]") {
|
||||
REQUIRE( print(char('a')) == "[ 'a' ]" );
|
||||
REQUIRE( print(bool(true)) == "[ true ]" );
|
||||
REQUIRE( print(bool(false)) == "[ false ]" );
|
||||
REQUIRE( print(short(123)) == "[ 123 ]" );
|
||||
REQUIRE( print(int(std::numeric_limits<int>::max() - 1)) == "[ 2147483646 ]" );
|
||||
REQUIRE( print(static_cast< unsigned int >(std::numeric_limits< int >::max()) + 1) == "[ 2147483648 ]" );
|
||||
|
||||
using Catch::Matchers::StartsWith;
|
||||
using Catch::Matchers::EndsWith;
|
||||
static const char PTR_TYPE[] = "abacus";
|
||||
REQUIRE_THAT( print(static_cast< const void * >(PTR_TYPE)), StartsWith("[ 0x") && EndsWith(" ]") );
|
||||
REQUIRE( print(static_cast< const char * >(PTR_TYPE)) == R"([ "abacus" ])" );
|
||||
REQUIRE( print(std::string("abacus")) == R"([ "abacus" ])" );
|
||||
|
||||
static const int INT_VAL = 100;
|
||||
REQUIRE_THAT( print(static_cast< const int * >(&INT_VAL)), StartsWith("[ 0x") && EndsWith(" ]") );
|
||||
REQUIRE( print(std::pair< int, std::string >(100, "abacus")) == R"([ [ 100 "abacus" ] ])" );
|
||||
REQUIRE( print(std::tuple< int, std::string, int >(100, "abacus", 123)) == R"([ [ 100 "abacus" 123 ] ])" );
|
||||
REQUIRE( print(std::map< std::string, char >{{"one", 'a'}, {"two", 'b'}, {"three", 'c'}})
|
||||
== R"([ [ [ "one" 'a' ] [ "three" 'c' ] [ "two" 'b' ] ] ])" );
|
||||
REQUIRE( print(PrintableType()) == "[ PrintableType -2 -1 ]" );
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::string printAttribute(const std::string& attr, const T &x) {
|
||||
std::ostringstream os;
|
||||
{
|
||||
Printer printer(os, -1, -1);
|
||||
printer.printAttribute(attr, x);
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
TEST_CASE("printable types, with attribute", "[printer]") {
|
||||
REQUIRE( printAttribute("fee", char('a')) == "[ fee = 'a' ]" );
|
||||
REQUIRE( printAttribute("fi", int(32)) == "[ fi = 32 ]" );
|
||||
REQUIRE( printAttribute("fo", std::map< std::string, char >{{"one", 'a'}, {"two", 'b'}, {"three", 'c'}})
|
||||
== R"([ fo = [ [ "one" 'a' ] [ "three" 'c' ] [ "two" 'b' ] ] ])" );
|
||||
REQUIRE( printAttribute("fum", PrintableType()) == "[ fum = PrintableType -2 -1 ]");
|
||||
}
|
||||
|
||||
void printAnother(Printer &) {}
|
||||
|
||||
template <typename T, typename... Tmore>
|
||||
void printAnother(Printer &p, const std::string &attr, const T& x, const Tmore&... more) {
|
||||
p.printAttribute(attr, x);
|
||||
printAnother(p, more...);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
std::string printMany(const T&... x) {
|
||||
std::ostringstream os;
|
||||
{
|
||||
Printer p(os, -1, -1);
|
||||
printAnother(p, x...);
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
TEST_CASE("printable types, with multiple attributes", "[printer]") {
|
||||
REQUIRE( printMany("val", 1, "v2", 2, "v3", 3, "str", std::string{"xxx"})
|
||||
== "[ val = 1 v2 = 2 v3 = 3 str = \"xxx\" ]" );
|
||||
REQUIRE( printMany("str", std::string{"xxx"}) == "[ str = \"xxx\" ]" );
|
||||
}
|
||||
|
Loading…
Reference in New Issue