Merge pull request #349 from michael-loki/printer

Create Printer - A general-purpose, stateful printer class
This commit is contained in:
Jeff 2019-02-26 17:40:24 -05:00 committed by GitHub
commit e8fb7d34c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1496 additions and 134 deletions

View File

@ -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
@ -27,6 +28,7 @@ set(LIB_UTIL_SRC
util/threadpool.cpp
util/time.cpp
util/timer.cpp
util/traits.cpp
util/types.cpp
)

View File

@ -95,6 +95,14 @@ namespace llarp
return out << "[secretkey]";
}
std::ostream &
print(std::ostream &stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printValue("secretkey");
return stream;
}
PubKey
toPublic() const
{

View File

@ -4,6 +4,7 @@
#include <util/buffer.hpp>
#include <util/endian.hpp>
#include <util/logger.hpp>
#include <util/printer.hpp>
#include <array>
@ -270,5 +271,20 @@ namespace llarp
}
}
std::ostream&
Message::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttributeAsHex("dns message id", hdr_id);
printer.printAttributeAsHex("fields", hdr_fields);
printer.printAttribute("questions", questions);
printer.printAttribute("answers", answers);
printer.printAttribute("nameserer", authorities);
printer.printAttribute("additional", additional);
return stream;
}
} // namespace dns
} // namespace llarp

View File

@ -72,24 +72,8 @@ namespace llarp
bool
Decode(llarp_buffer_t* buf) override;
friend std::ostream&
operator<<(std::ostream& out, const Message& msg)
{
out << "[dns message id=" << std::hex << msg.hdr_id
<< " fields=" << msg.hdr_fields << " questions=[ ";
for(const auto& qd : msg.questions)
out << qd << ", ";
out << "] answers=[ ";
for(const auto& an : msg.answers)
out << an << ", ";
out << "] nameserver=[ ";
for(const auto& ns : msg.authorities)
out << ns << ", ";
out << "] additional=[ ";
for(const auto& ar : msg.additional)
out << ar << ", ";
return out << "]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
MsgID_t hdr_id;
Fields_t hdr_fields;
@ -98,6 +82,13 @@ namespace llarp
std::vector< ResourceRecord > authorities;
std::vector< ResourceRecord > additional;
};
inline std::ostream&
operator<<(std::ostream& out, const Message& msg)
{
msg.print(out, -1, -1);
return out;
}
} // namespace dns
} // namespace llarp

View File

@ -1,6 +1,7 @@
#include <dns/question.hpp>
#include <util/logger.hpp>
#include <util/printer.hpp>
namespace llarp
{
@ -47,5 +48,16 @@ namespace llarp
}
return true;
}
std::ostream&
Question::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("qname", qname);
printer.printAttributeAsHex("qtype", qtype);
printer.printAttributeAsHex("qclass", qclass);
return stream;
}
} // namespace dns
} // namespace llarp

View File

@ -23,6 +23,9 @@ namespace llarp
bool
Decode(llarp_buffer_t* buf) override;
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
bool
operator==(const Question& other) const
{
@ -30,17 +33,17 @@ namespace llarp
&& qclass == other.qclass;
}
friend std::ostream&
operator<<(std::ostream& out, const Question& q)
{
return out << "qname=" << q.qname << " qtype=" << (int)q.qtype
<< " qclass=" << (int)q.qclass;
}
Name_t qname;
QType_t qtype;
QClass_t qclass;
};
inline std::ostream&
operator<<(std::ostream& out, const Question& q)
{
q.print(out, -1, -1);
return out;
}
} // namespace dns
} // namespace llarp

View File

@ -1,6 +1,7 @@
#include <dns/rr.hpp>
#include <util/logger.hpp>
#include <util/printer.hpp>
namespace llarp
{
@ -80,5 +81,18 @@ namespace llarp
}
return true;
}
std::ostream&
ResourceRecord::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("RR name", rr_name);
printer.printAttribute("type", rr_type);
printer.printAttribute("class", rr_class);
printer.printAttribute("ttl", ttl);
printer.printAttribute("rdata", rData.size());
return stream;
}
} // namespace dns
} // namespace llarp

View File

@ -29,13 +29,8 @@ namespace llarp
bool
Decode(llarp_buffer_t* buf) override;
friend std::ostream&
operator<<(std::ostream& out, const ResourceRecord& rr)
{
return out << "[RR name=" << rr.rr_name << " type=" << rr.rr_type
<< " class=" << rr.rr_class << " ttl=" << rr.ttl
<< " rdata=[" << rr.rData.size() << " bytes]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
Name_t rr_name;
RRType_t rr_type;
@ -43,6 +38,12 @@ namespace llarp
RR_TTL_t ttl;
RR_RData_t rData;
};
inline std::ostream&
operator<<(std::ostream& out, const ResourceRecord& rr)
{
return rr.print(out, -1, -1);
}
} // namespace dns
} // namespace llarp

View File

@ -6,6 +6,7 @@
#include <net/net.hpp>
#include <util/bencode.h>
#include <util/mem.h>
#include <util/printer.hpp>
#include <string.h>
@ -162,4 +163,17 @@ namespace llarp
/** end */
return bencode_end(buff);
}
std::ostream &
AddressInfo::print(std::ostream &stream, int level, int spaces) const
{
char tmp[128] = {0};
inet_ntop(AF_INET6, (void *)&ip, tmp, sizeof(tmp));
Printer printer(stream, level, spaces);
printer.printAttribute("ip", tmp);
printer.printAttribute("port", port);
return stream;
}
} // namespace llarp

View File

@ -52,19 +52,8 @@ namespace llarp
bool
DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf) override;
friend std::ostream&
operator<<(std::ostream& out, const AddressInfo& a)
{
char tmp[128] = {0};
inet_ntop(AF_INET6, (void*)&a.ip, tmp, sizeof(tmp));
out << tmp << ".";
#if defined(ANDROID) || defined(RPI)
snprintf(tmp, sizeof(tmp), "%u", a.port);
return out << tmp;
#else
return out << std::to_string(a.port);
#endif
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
struct Hash
{
@ -76,6 +65,12 @@ namespace llarp
};
};
inline std::ostream&
operator<<(std::ostream& out, const AddressInfo& a)
{
return a.print(out, -1, -1);
}
bool
operator==(const AddressInfo& lhs, const AddressInfo& rhs);

View File

@ -84,22 +84,27 @@ namespace llarp
}
std::ostream&
operator<<(std::ostream& out, const ExitInfo& xi)
ExitInfo::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
std::ostringstream ss;
char tmp[128] = {0};
if(inet_ntop(AF_INET6, (void*)&xi.address, tmp, sizeof(tmp)))
out << std::string(tmp);
if(inet_ntop(AF_INET6, (void*)&address, tmp, sizeof(tmp)))
ss << tmp;
else
return out;
out << std::string("/");
return stream;
ss << std::string("/");
#if defined(ANDROID) || defined(RPI)
snprintf(tmp, sizeof(tmp), "%zu",
llarp::bits::count_array_bits(xi.netmask.s6_addr));
return out << tmp;
llarp::bits::count_array_bits(netmask.s6_addr));
ss << tmp;
#else
return out << std::to_string(
llarp::bits::count_array_bits(xi.netmask.s6_addr));
ss << std::to_string(llarp::bits::count_array_bits(netmask.s6_addr));
#endif
printer.printValue(ss.str());
return stream;
}
} // namespace llarp

View File

@ -53,10 +53,16 @@ namespace llarp
ExitInfo &
operator=(const ExitInfo &other);
std::ostream &
print(std::ostream &stream, int level, int spaces) const;
};
std::ostream &
operator<<(std::ostream &out, const ExitInfo &xi);
inline std::ostream &
operator<<(std::ostream &out, const ExitInfo &xi)
{
return xi.print(out, -1, -1);
}
} // namespace llarp
#endif

View File

@ -18,6 +18,18 @@ namespace llarp
{
namespace path
{
std::ostream&
TransitHopInfo::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("tx", txID);
printer.printAttribute("rx", rxID);
printer.printAttribute("upstream", upstream);
printer.printAttribute("downstream", downstream);
return stream;
}
PathContext::PathContext(AbstractRouter* router)
: m_Router(router), m_AllowTransit(false)
{
@ -350,6 +362,17 @@ namespace llarp
RemovePathSet(ctx);
}
std::ostream&
TransitHop::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("TransitHop", info);
printer.printAttribute("started", started);
printer.printAttribute("lifetime", lifetime);
return stream;
}
PathHopConfig::PathHopConfig()
{
}

View File

@ -50,14 +50,8 @@ namespace llarp
RouterID upstream;
RouterID downstream;
friend std::ostream&
operator<<(std::ostream& out, const TransitHopInfo& info)
{
out << "<tx=" << info.txID << " rx=" << info.rxID;
out << " upstream=" << info.upstream
<< " downstream=" << info.downstream;
return out << ">";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
bool
operator==(const TransitHopInfo& other) const
@ -102,6 +96,12 @@ namespace llarp
};
};
inline std::ostream&
operator<<(std::ostream& out, const TransitHopInfo& info)
{
return info.print(out, -1, -1);
}
struct IHopHandler
{
virtual ~IHopHandler(){};
@ -172,12 +172,8 @@ namespace llarp
return m_LastActivity;
}
friend std::ostream&
operator<<(std::ostream& out, const TransitHop& h)
{
return out << "[TransitHop " << h.info << " started=" << h.started
<< " lifetime=" << h.lifetime << "]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
bool
Expired(llarp_time_t now) const override;
@ -268,6 +264,12 @@ namespace llarp
AbstractRouter* r) override;
};
inline std::ostream&
operator<<(std::ostream& out, const TransitHop& h)
{
return h.print(out, -1, -1);
}
/// configuration for a single hop when building a path
struct PathHopConfig : public util::IStateful
{

View File

@ -55,4 +55,16 @@ namespace llarp
return true;
}
std::ostream&
PoW::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("pow timestamp", timestamp);
printer.printAttribute("lifetime", extendedLifetime);
printer.printAttribute("nonce", nonce);
return stream;
}
} // namespace llarp

View File

@ -39,14 +39,15 @@ namespace llarp
return !(*this == other);
}
friend std::ostream&
operator<<(std::ostream& out, const PoW& p)
{
return out << "[pow timestamp=" << p.timestamp
<< " lifetime=" << p.extendedLifetime << " nonce=" << p.nonce
<< "]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
};
inline std::ostream&
operator<<(std::ostream& out, const PoW& p)
{
return p.print(out, -1, -1);
}
} // namespace llarp
#endif

View File

@ -7,6 +7,7 @@
#include <util/buffer.hpp>
#include <util/logger.hpp>
#include <util/mem.hpp>
#include <util/printer.hpp>
#include <util/time.hpp>
#include <fstream>
@ -382,4 +383,20 @@ namespace llarp
return *this;
}
std::ostream &
RouterContact::print(std::ostream &stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("k", pubkey);
printer.printAttribute("updated", last_updated);
printer.printAttribute("netid", netID);
printer.printAttribute("v", version);
printer.printAttribute("ai", addrs);
printer.printAttribute("xi", exits);
printer.printAttribute("e", enckey);
printer.printAttribute("z", signature);
return stream;
}
} // namespace llarp

View File

@ -40,10 +40,12 @@ namespace llarp
return !(*this == other);
}
friend std::ostream &
operator<<(std::ostream &out, const NetID &id)
std::ostream &
print(std::ostream &stream, int level, int spaces) const
{
return out << id.ToString();
Printer printer(stream, level, spaces);
printer.printValue(ToString());
return stream;
}
std::string
@ -56,6 +58,12 @@ namespace llarp
BEncode(llarp_buffer_t *buf) const;
};
inline std::ostream &
operator<<(std::ostream &out, const NetID &id)
{
return id.print(out, -1, -1);
}
/// RouterContact
struct RouterContact final : public IBEncodeMessage, public util::IStateful
{
@ -176,21 +184,8 @@ namespace llarp
return last_updated < other.last_updated;
}
friend std::ostream &
operator<<(std::ostream &out, const RouterContact &rc)
{
out << "[RouterContact k=" << rc.pubkey;
out << " updated=" << rc.last_updated;
out << " netid=" << rc.netID;
out << " v=" << rc.version;
out << " ai=[ ";
for(const auto &addr : rc.addrs)
out << addr << " ";
out << " ] xi=[ ";
for(const auto &xi : rc.exits)
out << xi << " ";
return out << " ] e=" << rc.enckey << " z=" << rc.signature << " ]";
}
std::ostream &
print(std::ostream &stream, int level, int spaces) const;
bool
Read(const char *fname);
@ -203,6 +198,12 @@ namespace llarp
VerifySignature(llarp::Crypto *crypto) const;
};
inline std::ostream &
operator<<(std::ostream &out, const RouterContact &rc)
{
return rc.print(out, -1, -1);
}
using RouterLookupHandler =
std::function< void(const std::vector< RouterContact > &) >;
} // namespace llarp

View File

@ -105,12 +105,8 @@ namespace llarp
return Addr() < other.Addr();
}
friend std::ostream&
operator<<(std::ostream& out, const ServiceInfo& i)
{
return out << "[e=" << i.enckey << " s=" << i.signkey
<< " v=" << i.version << " x=" << i.vanity << "]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
/// .loki address
std::string
@ -145,6 +141,12 @@ namespace llarp
private:
Address m_CachedAddr;
};
inline std::ostream&
operator<<(std::ostream& out, const ServiceInfo& i)
{
return i.print(out, -1, -1);
}
} // namespace service
} // namespace llarp

View File

@ -65,5 +65,17 @@ namespace llarp
latency = 0;
expiresAt = 0;
}
std::ostream&
Introduction::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("k", router);
printer.printAttribute("p", pathID);
printer.printAttribute("v", version);
printer.printAttribute("x", expiresAt);
return stream;
}
} // namespace service
} // namespace llarp

View File

@ -50,12 +50,8 @@ namespace llarp
~Introduction();
friend std::ostream&
operator<<(std::ostream& out, const Introduction& i)
{
return out << "k=" << i.router << " p=" << i.pathID
<< " v=" << i.version << " x=" << i.expiresAt;
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
bool
BEncode(llarp_buffer_t* buf) const override;
@ -95,6 +91,12 @@ namespace llarp
}
};
};
inline std::ostream&
operator<<(std::ostream& out, const Introduction& i)
{
return i.print(out, -1, -1);
}
} // namespace service
} // namespace llarp

View File

@ -179,5 +179,32 @@ namespace llarp
t = std::max(intro.expiresAt, t);
return t;
}
std::ostream&
IntroSet::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("A", A);
printer.printAttribute("I", I);
printer.printAttribute("K", K);
std::string _topic = topic.ToString();
if(!_topic.empty())
{
printer.printAttribute("topic", _topic);
}
else
{
printer.printAttribute("topic", topic);
}
printer.printAttribute("T", T);
printer.printAttribute("W", W);
printer.printAttribute("V", version);
printer.printAttribute("Z", Z);
return stream;
}
} // namespace service
} // namespace llarp

View File

@ -114,32 +114,8 @@ namespace llarp
return T < other.T;
}
friend std::ostream&
operator<<(std::ostream& out, const IntroSet& i)
{
out << "A=[" << i.A << "] I=[";
for(const auto& intro : i.I)
{
out << intro << ", ";
}
out << "]";
out << "K=" << i.K;
auto topic = i.topic.ToString();
if(topic.size())
{
out << " topic=" << topic;
}
else
{
out << " topic=" << i.topic;
}
out << " T=" << i.T;
if(i.W)
{
out << " W=" << *i.W;
}
return out << " V=" << i.version << " Z=" << i.Z;
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
llarp_time_t
GetNewestIntroExpiration() const;
@ -160,6 +136,12 @@ namespace llarp
Verify(llarp::Crypto* crypto, llarp_time_t now) const;
};
inline std::ostream&
operator<<(std::ostream& out, const IntroSet& i)
{
return i.print(out, -1, -1);
}
using IntroSetLookupHandler =
std::function< void(const std::vector< IntroSet >&) >;

View File

@ -82,5 +82,17 @@ namespace llarp
return CalculateAddress(m_CachedAddr.as_array());
}
std::ostream&
ServiceInfo::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("e", enckey);
printer.printAttribute("s", signkey);
printer.printAttribute("v", version);
printer.printAttribute("x", vanity);
return stream;
}
} // namespace service
} // namespace llarp

View File

@ -4,6 +4,8 @@
#include <util/bencode.h>
#include <util/encode.hpp>
#include <util/logger.hpp>
#include <util/printer.hpp>
#include <util/traits.hpp>
#include <array>
#include <cstddef>
@ -257,6 +259,15 @@ namespace llarp
return std::string(HexEncode(*this, strbuf));
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printValue(ToHex());
return stream;
}
struct Hash
{
size_t
@ -273,7 +284,6 @@ namespace llarp
type; // why did we align to the nearest double-precision float
AlignedStorage val;
};
} // namespace llarp
#endif

182
llarp/util/printer.cpp Normal file
View File

@ -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

511
llarp/util/printer.hpp Normal file
View File

@ -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

1
llarp/util/traits.cpp Normal file
View File

@ -0,0 +1 @@
#include <util/traits.hpp>

197
llarp/util/traits.hpp Normal file
View File

@ -0,0 +1,197 @@
#ifndef LLARP_TRAITS_HPP
#define LLARP_TRAITS_HPP
#include <type_traits>
#include <utility>
namespace llarp
{
namespace traits
{
/// Represents the empty type
struct Bottom
{
};
/// C++17 compatibility. template pack
template < class... >
using void_t = void;
/// 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,
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
#endif

View File

@ -34,9 +34,11 @@ 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
util/test_llarp_util_traits.cpp
)
add_executable(${TEST_EXE}

View File

@ -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), );

View File

@ -0,0 +1,154 @@
#include <util/traits.hpp>
#include <list>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
using namespace llarp;
TEST(traits_bottom, Smoke)
{
traits::Bottom bottom;
(void)bottom;
SUCCEED();
}
template < typename T >
class IsContainer : public ::testing::Test
{
};
TYPED_TEST_SUITE_P(IsContainer);
TYPED_TEST_P(IsContainer, Smoke)
{
bool expected = std::tuple_element_t< 1, TypeParam >::value;
bool result =
traits::is_container< std::tuple_element_t< 0, TypeParam > >::value;
ASSERT_EQ(expected, result);
}
REGISTER_TYPED_TEST_SUITE_P(IsContainer, Smoke);
// clang-format off
using ContainerTypes = ::testing::Types<
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 > >
>;
INSTANTIATE_TYPED_TEST_SUITE_P(traits, IsContainer, ContainerTypes);
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
template < typename T >
class TestSwitch : public ::testing::Test
{
};
TYPED_TEST_SUITE_P(TestSwitch);
TYPED_TEST_P(TestSwitch, Smoke)
{
char expected = std::tuple_element_t< 0, TypeParam >::value;
using InputType = typename std::tuple_element_t< 1, TypeParam >::Type;
char result = f(InputType());
ASSERT_EQ(expected, result);
}
REGISTER_TYPED_TEST_SUITE_P(TestSwitch, Smoke);
// clang-format off
using namespace traits::Switch;
using SwitchTypes = ::testing::Types<
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 > >
>;
INSTANTIATE_TYPED_TEST_SUITE_P(traits, TestSwitch, SwitchTypes);
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());
}
template < typename T >
class Select : public ::testing::Test
{
};
TYPED_TEST_SUITE_P(Select);
TYPED_TEST_P(Select, Smoke)
{
char expected = std::tuple_element_t< 0, TypeParam >::value;
char result = selectCase<std::tuple_element_t< 1, TypeParam > >();
ASSERT_EQ(expected, result);
}
REGISTER_TYPED_TEST_SUITE_P(Select, Smoke);
using SelectTypes = ::testing::Types<
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 >
>;
INSTANTIATE_TYPED_TEST_SUITE_P(traits, Select, SelectTypes);
// clang-format on