add basic discord support
This commit is contained in:
127
DPP/mlspp/lib/bytes/include/bytes/bytes.h
Executable file
127
DPP/mlspp/lib/bytes/include/bytes/bytes.h
Executable file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <tls/tls_syntax.h>
|
||||
#include <vector>
|
||||
|
||||
namespace mlspp::bytes_ns {
|
||||
|
||||
struct bytes
|
||||
{
|
||||
// Ensure defaults
|
||||
bytes() = default;
|
||||
bytes(const bytes&) = default;
|
||||
bytes& operator=(const bytes&) = default;
|
||||
bytes(bytes&&) = default;
|
||||
bytes& operator=(bytes&&) = default;
|
||||
|
||||
// Zeroize on drop
|
||||
~bytes()
|
||||
{
|
||||
auto ptr = static_cast<volatile uint8_t*>(_data.data());
|
||||
std::fill(ptr, ptr + _data.size(), uint8_t(0));
|
||||
}
|
||||
|
||||
// Mimic std::vector ctors
|
||||
bytes(size_t count, const uint8_t& value = 0)
|
||||
: _data(count, value)
|
||||
{
|
||||
}
|
||||
|
||||
bytes(std::initializer_list<uint8_t> init)
|
||||
: _data(init)
|
||||
{
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
bytes(const std::array<uint8_t, N>& data)
|
||||
: _data(data.begin(), data.end())
|
||||
{
|
||||
}
|
||||
|
||||
// Slice out sub-vectors (to avoid an iterator ctor)
|
||||
bytes slice(size_t begin_index, size_t end_index) const
|
||||
{
|
||||
const auto begin_it = _data.begin() + begin_index;
|
||||
const auto end_it = _data.begin() + end_index;
|
||||
return std::vector<uint8_t>(begin_it, end_it);
|
||||
}
|
||||
|
||||
// Freely convert to/from std::vector
|
||||
bytes(const std::vector<uint8_t>& vec)
|
||||
: _data(vec)
|
||||
{
|
||||
}
|
||||
|
||||
bytes(std::vector<uint8_t>&& vec)
|
||||
: _data(vec)
|
||||
{
|
||||
}
|
||||
|
||||
operator const std::vector<uint8_t>&() const { return _data; }
|
||||
operator std::vector<uint8_t>&() { return _data; }
|
||||
operator std::vector<uint8_t>&&() && { return std::move(_data); }
|
||||
|
||||
const std::vector<uint8_t>& as_vec() const { return _data; }
|
||||
std::vector<uint8_t>& as_vec() { return _data; }
|
||||
|
||||
// Pass through methods
|
||||
auto data() const { return _data.data(); }
|
||||
auto data() { return _data.data(); }
|
||||
|
||||
auto size() const { return _data.size(); }
|
||||
auto empty() const { return _data.empty(); }
|
||||
|
||||
auto begin() const { return _data.begin(); }
|
||||
auto begin() { return _data.begin(); }
|
||||
|
||||
auto end() const { return _data.end(); }
|
||||
auto end() { return _data.end(); }
|
||||
|
||||
const auto& at(size_t pos) const { return _data.at(pos); }
|
||||
auto& at(size_t pos) { return _data.at(pos); }
|
||||
|
||||
void resize(size_t count) { _data.resize(count); }
|
||||
void reserve(size_t len) { _data.reserve(len); }
|
||||
void push_back(uint8_t byte) { _data.push_back(byte); }
|
||||
|
||||
// Equality operators
|
||||
bool operator==(const bytes& other) const;
|
||||
bool operator!=(const bytes& other) const;
|
||||
|
||||
bool operator==(const std::vector<uint8_t>& other) const;
|
||||
bool operator!=(const std::vector<uint8_t>& other) const;
|
||||
|
||||
// Arithmetic operators
|
||||
bytes& operator+=(const bytes& other);
|
||||
bytes operator+(const bytes& rhs) const;
|
||||
bytes operator^(const bytes& rhs) const;
|
||||
|
||||
// Sorting operators (to allow usage as map keys)
|
||||
bool operator<(const bytes& rhs) const;
|
||||
|
||||
// Other, external operators
|
||||
friend std::ostream& operator<<(std::ostream& out, const bytes& data);
|
||||
friend bool operator==(const std::vector<uint8_t>& lhs, const bytes& rhs);
|
||||
friend bool operator!=(const std::vector<uint8_t>& lhs, const bytes& rhs);
|
||||
|
||||
// TLS syntax serialization
|
||||
TLS_SERIALIZABLE(_data);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> _data;
|
||||
};
|
||||
|
||||
std::string
|
||||
to_ascii(const bytes& data);
|
||||
|
||||
bytes
|
||||
from_ascii(const std::string& ascii);
|
||||
|
||||
std::string
|
||||
to_hex(const bytes& data);
|
||||
|
||||
bytes
|
||||
from_hex(const std::string& hex);
|
||||
|
||||
} // namespace mlspp::bytes_ns
|
||||
61
DPP/mlspp/lib/bytes/include/tls/compat.h
Executable file
61
DPP/mlspp/lib/bytes/include/tls/compat.h
Executable file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef VARIANT_COMPAT
|
||||
#include <variant.hpp>
|
||||
#else
|
||||
#include <variant>
|
||||
#endif // VARIANT_COMPAT
|
||||
|
||||
namespace mlspp::tls {
|
||||
|
||||
namespace var = std;
|
||||
|
||||
// In a similar vein, we provide our own safe accessors for std::optional, since
|
||||
// std::optional::value() is not available on macOS 10.11.
|
||||
namespace opt {
|
||||
|
||||
template<typename T>
|
||||
T&
|
||||
get(std::optional<T>& opt)
|
||||
{
|
||||
if (!opt) {
|
||||
throw std::runtime_error("bad_optional_access");
|
||||
}
|
||||
return *opt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T&
|
||||
get(const std::optional<T>& opt)
|
||||
{
|
||||
if (!opt) {
|
||||
throw std::runtime_error("bad_optional_access");
|
||||
}
|
||||
return *opt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T&&
|
||||
get(std::optional<T>&& opt)
|
||||
{
|
||||
if (!opt) {
|
||||
throw std::runtime_error("bad_optional_access");
|
||||
}
|
||||
return std::move(*opt);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T&&
|
||||
get(const std::optional<T>&& opt)
|
||||
{
|
||||
if (!opt) {
|
||||
throw std::runtime_error("bad_optional_access");
|
||||
}
|
||||
return std::move(*opt);
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace mlspp::tls
|
||||
569
DPP/mlspp/lib/bytes/include/tls/tls_syntax.h
Executable file
569
DPP/mlspp/lib/bytes/include/tls/tls_syntax.h
Executable file
@@ -0,0 +1,569 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include <tls/compat.h>
|
||||
|
||||
namespace mlspp::tls {
|
||||
|
||||
// For indicating no min or max in vector definitions
|
||||
const size_t none = std::numeric_limits<size_t>::max();
|
||||
|
||||
class WriteError : public std::invalid_argument
|
||||
{
|
||||
public:
|
||||
using parent = std::invalid_argument;
|
||||
using parent::parent;
|
||||
};
|
||||
|
||||
class ReadError : public std::invalid_argument
|
||||
{
|
||||
public:
|
||||
using parent = std::invalid_argument;
|
||||
using parent::parent;
|
||||
};
|
||||
|
||||
///
|
||||
/// Declarations of Streams and Traits
|
||||
///
|
||||
|
||||
class ostream
|
||||
{
|
||||
public:
|
||||
static const size_t none = std::numeric_limits<size_t>::max();
|
||||
|
||||
void write_raw(const std::vector<uint8_t>& bytes);
|
||||
|
||||
const std::vector<uint8_t>& bytes() const { return _buffer; }
|
||||
size_t size() const { return _buffer.size(); }
|
||||
bool empty() const { return _buffer.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> _buffer;
|
||||
ostream& write_uint(uint64_t value, int length);
|
||||
|
||||
friend ostream& operator<<(ostream& out, bool data);
|
||||
friend ostream& operator<<(ostream& out, uint8_t data);
|
||||
friend ostream& operator<<(ostream& out, uint16_t data);
|
||||
friend ostream& operator<<(ostream& out, uint32_t data);
|
||||
friend ostream& operator<<(ostream& out, uint64_t data);
|
||||
|
||||
template<typename T>
|
||||
friend ostream& operator<<(ostream& out, const std::vector<T>& data);
|
||||
|
||||
friend struct varint;
|
||||
};
|
||||
|
||||
class istream
|
||||
{
|
||||
public:
|
||||
istream(const std::vector<uint8_t>& data)
|
||||
: _buffer(data)
|
||||
{
|
||||
// So that we can use the constant-time pop_back
|
||||
std::reverse(_buffer.begin(), _buffer.end());
|
||||
}
|
||||
|
||||
size_t size() const { return _buffer.size(); }
|
||||
bool empty() const { return _buffer.empty(); }
|
||||
|
||||
std::vector<uint8_t> bytes()
|
||||
{
|
||||
auto bytes = _buffer;
|
||||
std::reverse(bytes.begin(), bytes.end());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private:
|
||||
istream() {}
|
||||
std::vector<uint8_t> _buffer;
|
||||
uint8_t next();
|
||||
|
||||
template<typename T>
|
||||
istream& read_uint(T& data, size_t length)
|
||||
{
|
||||
uint64_t value = 0;
|
||||
for (size_t i = 0; i < length; i += 1) {
|
||||
value = (value << unsigned(8)) + next();
|
||||
}
|
||||
data = static_cast<T>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend istream& operator>>(istream& in, bool& data);
|
||||
friend istream& operator>>(istream& in, uint8_t& data);
|
||||
friend istream& operator>>(istream& in, uint16_t& data);
|
||||
friend istream& operator>>(istream& in, uint32_t& data);
|
||||
friend istream& operator>>(istream& in, uint64_t& data);
|
||||
|
||||
template<typename T>
|
||||
friend istream& operator>>(istream& in, std::vector<T>& data);
|
||||
|
||||
friend struct varint;
|
||||
};
|
||||
|
||||
// Traits must have static encode and decode methods, of the following form:
|
||||
//
|
||||
// static ostream& encode(ostream& str, const T& val);
|
||||
// static istream& decode(istream& str, T& val);
|
||||
//
|
||||
// Trait types will never be constructed; only these static methods are used.
|
||||
// The value arguments to encode and decode can be as strict or as loose as
|
||||
// desired.
|
||||
//
|
||||
// Ultimately, all interesting encoding should be done through traits.
|
||||
//
|
||||
// * vectors
|
||||
// * variants
|
||||
// * varints
|
||||
|
||||
struct pass
|
||||
{
|
||||
template<typename T>
|
||||
static ostream& encode(ostream& str, const T& val);
|
||||
|
||||
template<typename T>
|
||||
static istream& decode(istream& str, T& val);
|
||||
};
|
||||
|
||||
template<typename Ts>
|
||||
struct variant
|
||||
{
|
||||
template<typename... Tp>
|
||||
static inline Ts type(const var::variant<Tp...>& data);
|
||||
|
||||
template<typename... Tp>
|
||||
static ostream& encode(ostream& str, const var::variant<Tp...>& data);
|
||||
|
||||
template<size_t I = 0, typename Te, typename... Tp>
|
||||
static inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
read_variant(istream&, Te, var::variant<Tp...>&);
|
||||
|
||||
template<size_t I = 0, typename Te, typename... Tp>
|
||||
static inline typename std::enable_if <
|
||||
I<sizeof...(Tp), void>::type read_variant(istream& str,
|
||||
Te target_type,
|
||||
var::variant<Tp...>& v);
|
||||
|
||||
template<typename... Tp>
|
||||
static istream& decode(istream& str, var::variant<Tp...>& data);
|
||||
};
|
||||
|
||||
struct varint
|
||||
{
|
||||
static ostream& encode(ostream& str, const uint64_t& val);
|
||||
static istream& decode(istream& str, uint64_t& val);
|
||||
};
|
||||
|
||||
///
|
||||
/// Writer implementations
|
||||
///
|
||||
|
||||
// Primitive writers defined in .cpp file
|
||||
|
||||
// Array writer
|
||||
template<typename T, size_t N>
|
||||
ostream&
|
||||
operator<<(ostream& out, const std::array<T, N>& data)
|
||||
{
|
||||
for (const auto& item : data) {
|
||||
out << item;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Optional writer
|
||||
template<typename T>
|
||||
ostream&
|
||||
operator<<(ostream& out, const std::optional<T>& opt)
|
||||
{
|
||||
if (!opt) {
|
||||
return out << uint8_t(0);
|
||||
}
|
||||
|
||||
return out << uint8_t(1) << opt::get(opt);
|
||||
}
|
||||
|
||||
// Enum writer
|
||||
template<typename T, std::enable_if_t<std::is_enum<T>::value, int> = 0>
|
||||
ostream&
|
||||
operator<<(ostream& str, const T& val)
|
||||
{
|
||||
auto u = static_cast<std::underlying_type_t<T>>(val);
|
||||
return str << u;
|
||||
}
|
||||
|
||||
// Vector writer
|
||||
template<typename T>
|
||||
ostream&
|
||||
operator<<(ostream& str, const std::vector<T>& vec)
|
||||
{
|
||||
// Pre-encode contents
|
||||
ostream temp;
|
||||
for (const auto& item : vec) {
|
||||
temp << item;
|
||||
}
|
||||
|
||||
// Write the encoded length, then the pre-encoded data
|
||||
varint::encode(str, temp._buffer.size());
|
||||
str.write_raw(temp.bytes());
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
///
|
||||
/// Reader implementations
|
||||
///
|
||||
|
||||
// Primitive type readers defined in .cpp file
|
||||
|
||||
// Array reader
|
||||
template<typename T, size_t N>
|
||||
istream&
|
||||
operator>>(istream& in, std::array<T, N>& data)
|
||||
{
|
||||
for (auto& item : data) {
|
||||
in >> item;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
// Optional reader
|
||||
template<typename T>
|
||||
istream&
|
||||
operator>>(istream& in, std::optional<T>& opt)
|
||||
{
|
||||
uint8_t present = 0;
|
||||
in >> present;
|
||||
|
||||
switch (present) {
|
||||
case 0:
|
||||
opt.reset();
|
||||
return in;
|
||||
|
||||
case 1:
|
||||
opt.emplace();
|
||||
return in >> opt::get(opt);
|
||||
|
||||
default:
|
||||
throw std::invalid_argument("Malformed optional");
|
||||
}
|
||||
}
|
||||
|
||||
// Enum reader
|
||||
// XXX(rlb): It would be nice if this could enforce that the values are valid,
|
||||
// but C++ doesn't seem to have that ability. When used as a tag for variants,
|
||||
// the variant reader will enforce, at least.
|
||||
template<typename T, std::enable_if_t<std::is_enum<T>::value, int> = 0>
|
||||
istream&
|
||||
operator>>(istream& str, T& val)
|
||||
{
|
||||
std::underlying_type_t<T> u;
|
||||
str >> u;
|
||||
val = static_cast<T>(u);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Vector reader
|
||||
template<typename T>
|
||||
istream&
|
||||
operator>>(istream& str, std::vector<T>& vec)
|
||||
{
|
||||
// Read the encoded data size
|
||||
auto size = uint64_t(0);
|
||||
varint::decode(str, size);
|
||||
if (size > str._buffer.size()) {
|
||||
throw ReadError("Vector is longer than remaining data");
|
||||
}
|
||||
|
||||
// Read the elements of the vector
|
||||
// NB: Remember that we store the vector in reverse order
|
||||
// NB: This requires that T be default-constructible
|
||||
istream r;
|
||||
r._buffer =
|
||||
std::vector<uint8_t>{ str._buffer.end() - size, str._buffer.end() };
|
||||
|
||||
vec.clear();
|
||||
while (r._buffer.size() > 0) {
|
||||
vec.emplace_back();
|
||||
r >> vec.back();
|
||||
}
|
||||
|
||||
// Truncate the primary buffer
|
||||
str._buffer.erase(str._buffer.end() - size, str._buffer.end());
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// Abbreviations
|
||||
template<typename T>
|
||||
std::vector<uint8_t>
|
||||
marshal(const T& value)
|
||||
{
|
||||
ostream w;
|
||||
w << value;
|
||||
return w.bytes();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
unmarshal(const std::vector<uint8_t>& data, T& value)
|
||||
{
|
||||
istream r(data);
|
||||
r >> value;
|
||||
}
|
||||
|
||||
template<typename T, typename... Tp>
|
||||
T
|
||||
get(const std::vector<uint8_t>& data, Tp... args)
|
||||
{
|
||||
T value(args...);
|
||||
unmarshal(data, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Use this macro to define struct serialization with minimal boilerplate
|
||||
#define TLS_SERIALIZABLE(...) \
|
||||
static const bool _tls_serializable = true; \
|
||||
auto _tls_fields_r() \
|
||||
{ \
|
||||
return std::forward_as_tuple(__VA_ARGS__); \
|
||||
} \
|
||||
auto _tls_fields_w() const \
|
||||
{ \
|
||||
return std::forward_as_tuple(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
// If your struct contains nontrivial members (e.g., vectors), use this to
|
||||
// define traits for them.
|
||||
#define TLS_TRAITS(...) \
|
||||
static const bool _tls_has_traits = true; \
|
||||
using _tls_traits = std::tuple<__VA_ARGS__>;
|
||||
|
||||
template<typename T>
|
||||
struct is_serializable
|
||||
{
|
||||
template<typename U>
|
||||
static std::true_type test(decltype(U::_tls_serializable));
|
||||
|
||||
template<typename U>
|
||||
static std::false_type test(...);
|
||||
|
||||
static const bool value = decltype(test<T>(true))::value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct has_traits
|
||||
{
|
||||
template<typename U>
|
||||
static std::true_type test(decltype(U::_tls_has_traits));
|
||||
|
||||
template<typename U>
|
||||
static std::false_type test(...);
|
||||
|
||||
static const bool value = decltype(test<T>(true))::value;
|
||||
};
|
||||
|
||||
///
|
||||
/// Trait implementations
|
||||
///
|
||||
|
||||
// Pass-through (normal encoding/decoding)
|
||||
template<typename T>
|
||||
ostream&
|
||||
pass::encode(ostream& str, const T& val)
|
||||
{
|
||||
return str << val;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
istream&
|
||||
pass::decode(istream& str, T& val)
|
||||
{
|
||||
return str >> val;
|
||||
}
|
||||
|
||||
// Variant encoding
|
||||
template<typename Ts, typename Tv>
|
||||
constexpr Ts
|
||||
variant_map();
|
||||
|
||||
#define TLS_VARIANT_MAP(EnumType, MappedType, enum_value) \
|
||||
template<> \
|
||||
constexpr EnumType variant_map<EnumType, MappedType>() \
|
||||
{ \
|
||||
return EnumType::enum_value; \
|
||||
}
|
||||
|
||||
template<typename Ts>
|
||||
template<typename... Tp>
|
||||
inline Ts
|
||||
variant<Ts>::type(const var::variant<Tp...>& data)
|
||||
{
|
||||
const auto get_type = [](const auto& v) {
|
||||
return variant_map<Ts, std::decay_t<decltype(v)>>();
|
||||
};
|
||||
return var::visit(get_type, data);
|
||||
}
|
||||
|
||||
template<typename Ts>
|
||||
template<typename... Tp>
|
||||
ostream&
|
||||
variant<Ts>::encode(ostream& str, const var::variant<Tp...>& data)
|
||||
{
|
||||
const auto write_variant = [&str](auto&& v) {
|
||||
using Tv = std::decay_t<decltype(v)>;
|
||||
str << variant_map<Ts, Tv>() << v;
|
||||
};
|
||||
var::visit(write_variant, data);
|
||||
return str;
|
||||
}
|
||||
|
||||
template<typename Ts>
|
||||
template<size_t I, typename Te, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
variant<Ts>::read_variant(istream&, Te, var::variant<Tp...>&)
|
||||
{
|
||||
throw ReadError("Invalid variant type label");
|
||||
}
|
||||
|
||||
template<typename Ts>
|
||||
template<size_t I, typename Te, typename... Tp>
|
||||
inline
|
||||
typename std::enable_if < I<sizeof...(Tp), void>::type
|
||||
variant<Ts>::read_variant(istream& str,
|
||||
Te target_type,
|
||||
var::variant<Tp...>& v)
|
||||
{
|
||||
using Tc = var::variant_alternative_t<I, var::variant<Tp...>>;
|
||||
if (variant_map<Ts, Tc>() == target_type) {
|
||||
str >> v.template emplace<I>();
|
||||
return;
|
||||
}
|
||||
|
||||
read_variant<I + 1>(str, target_type, v);
|
||||
}
|
||||
|
||||
template<typename Ts>
|
||||
template<typename... Tp>
|
||||
istream&
|
||||
variant<Ts>::decode(istream& str, var::variant<Tp...>& data)
|
||||
{
|
||||
Ts target_type;
|
||||
str >> target_type;
|
||||
read_variant(str, target_type, data);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Struct writer without traits (enabled by macro)
|
||||
template<size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
write_tuple(ostream&, const std::tuple<Tp...>&)
|
||||
{
|
||||
}
|
||||
|
||||
template<size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if <
|
||||
I<sizeof...(Tp), void>::type
|
||||
write_tuple(ostream& str, const std::tuple<Tp...>& t)
|
||||
{
|
||||
str << std::get<I>(t);
|
||||
write_tuple<I + 1, Tp...>(str, t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline
|
||||
typename std::enable_if<is_serializable<T>::value && !has_traits<T>::value,
|
||||
ostream&>::type
|
||||
operator<<(ostream& str, const T& obj)
|
||||
{
|
||||
write_tuple(str, obj._tls_fields_w());
|
||||
return str;
|
||||
}
|
||||
|
||||
// Struct writer with traits (enabled by macro)
|
||||
template<typename Tr, size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
write_tuple_traits(ostream&, const std::tuple<Tp...>&)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Tr, size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if <
|
||||
I<sizeof...(Tp), void>::type
|
||||
write_tuple_traits(ostream& str, const std::tuple<Tp...>& t)
|
||||
{
|
||||
std::tuple_element_t<I, Tr>::encode(str, std::get<I>(t));
|
||||
write_tuple_traits<Tr, I + 1, Tp...>(str, t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline
|
||||
typename std::enable_if<is_serializable<T>::value && has_traits<T>::value,
|
||||
ostream&>::type
|
||||
operator<<(ostream& str, const T& obj)
|
||||
{
|
||||
write_tuple_traits<typename T::_tls_traits>(str, obj._tls_fields_w());
|
||||
return str;
|
||||
}
|
||||
|
||||
// Struct reader without traits (enabled by macro)
|
||||
template<size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
read_tuple(istream&, const std::tuple<Tp...>&)
|
||||
{
|
||||
}
|
||||
|
||||
template<size_t I = 0, typename... Tp>
|
||||
inline
|
||||
typename std::enable_if < I<sizeof...(Tp), void>::type
|
||||
read_tuple(istream& str, const std::tuple<Tp...>& t)
|
||||
{
|
||||
str >> std::get<I>(t);
|
||||
read_tuple<I + 1, Tp...>(str, t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline
|
||||
typename std::enable_if<is_serializable<T>::value && !has_traits<T>::value,
|
||||
istream&>::type
|
||||
operator>>(istream& str, T& obj)
|
||||
{
|
||||
read_tuple(str, obj._tls_fields_r());
|
||||
return str;
|
||||
}
|
||||
|
||||
// Struct reader with traits (enabled by macro)
|
||||
template<typename Tr, size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
read_tuple_traits(istream&, const std::tuple<Tp...>&)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Tr, size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if <
|
||||
I<sizeof...(Tp), void>::type
|
||||
read_tuple_traits(istream& str, const std::tuple<Tp...>& t)
|
||||
{
|
||||
std::tuple_element_t<I, Tr>::decode(str, std::get<I>(t));
|
||||
read_tuple_traits<Tr, I + 1, Tp...>(str, t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline
|
||||
typename std::enable_if<is_serializable<T>::value && has_traits<T>::value,
|
||||
istream&>::type
|
||||
operator>>(istream& str, T& obj)
|
||||
{
|
||||
read_tuple_traits<typename T::_tls_traits>(str, obj._tls_fields_r());
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace mlspp::tls
|
||||
Reference in New Issue
Block a user