567 lines
18 KiB
C++
567 lines
18 KiB
C++
#pragma once
|
|
#include <vector>
|
|
#include <fc/optional.hpp>
|
|
#include <fc/string.hpp>
|
|
#include <memory>
|
|
#include <string.h> // memset
|
|
#include <unordered_set>
|
|
#include <unordered_map>
|
|
#include <map>
|
|
#include <set>
|
|
#include <fc/container/flat_fwd.hpp>
|
|
#include <fc/static_variant.hpp>
|
|
|
|
namespace fc
|
|
{
|
|
/**
|
|
* @defgroup serializable Serializable _types
|
|
* @brief Clas_ses that may be converted to/from an variant
|
|
*
|
|
* To make a class 'serializable' the following methods must be available
|
|
* for your Serializable_type
|
|
*
|
|
* @code
|
|
* void to_variant( const Serializable_type& e, variant& v );
|
|
* void from_variant( const variant& e, Serializable_type& ll );
|
|
* @endcode
|
|
*/
|
|
|
|
class variant;
|
|
class variant_object;
|
|
class mutable_variant_object;
|
|
class time_point;
|
|
class time_point_sec;
|
|
class microseconds;
|
|
template<typename T> struct safe;
|
|
|
|
struct blob { std::vector<char> data; };
|
|
|
|
void to_variant( const blob& var, variant& vo );
|
|
void from_variant( const variant& var, blob& vo );
|
|
template<typename T> void to_variant( const safe<T>& s, variant& v );
|
|
template<typename T> void from_variant( const variant& v, safe<T>& s );
|
|
|
|
template<typename... T> void to_variant( const static_variant<T...>& s, variant& v );
|
|
template<typename... T> void from_variant( const variant& v, static_variant<T...>& s );
|
|
|
|
void to_variant( const uint8_t& var, variant& vo );
|
|
void from_variant( const variant& var, uint8_t& vo );
|
|
void to_variant( const int8_t& var, variant& vo );
|
|
void from_variant( const variant& var, int8_t& vo );
|
|
|
|
void to_variant( const uint16_t& var, variant& vo );
|
|
void from_variant( const variant& var, uint16_t& vo );
|
|
void to_variant( const int16_t& var, variant& vo );
|
|
void from_variant( const variant& var, int16_t& vo );
|
|
|
|
void to_variant( const uint32_t& var, variant& vo );
|
|
void from_variant( const variant& var, uint32_t& vo );
|
|
void to_variant( const int32_t& var, variant& vo );
|
|
void from_variant( const variant& var, int32_t& vo );
|
|
|
|
void to_variant( const variant_object& var, variant& vo );
|
|
void from_variant( const variant& var, variant_object& vo );
|
|
void to_variant( const mutable_variant_object& var, variant& vo );
|
|
void from_variant( const variant& var, mutable_variant_object& vo );
|
|
void to_variant( const std::vector<char>& var, variant& vo );
|
|
void from_variant( const variant& var, std::vector<char>& vo );
|
|
|
|
template<typename K, typename T>
|
|
void to_variant( const std::unordered_map<K,T>& var, variant& vo );
|
|
template<typename K, typename T>
|
|
void from_variant( const variant& var, std::unordered_map<K,T>& vo );
|
|
|
|
template<typename K, typename T>
|
|
void to_variant( const fc::flat_map<K,T>& var, variant& vo );
|
|
template<typename K, typename T>
|
|
void from_variant( const variant& var, fc::flat_map<K,T>& vo );
|
|
|
|
template<typename K, typename T>
|
|
void to_variant( const std::map<K,T>& var, variant& vo );
|
|
template<typename K, typename T>
|
|
void from_variant( const variant& var, std::map<K,T>& vo );
|
|
template<typename K, typename T>
|
|
void to_variant( const std::multimap<K,T>& var, variant& vo );
|
|
template<typename K, typename T>
|
|
void from_variant( const variant& var, std::multimap<K,T>& vo );
|
|
|
|
|
|
template<typename T>
|
|
void to_variant( const std::unordered_set<T>& var, variant& vo );
|
|
template<typename T>
|
|
void from_variant( const variant& var, std::unordered_set<T>& vo );
|
|
|
|
template<typename T>
|
|
void to_variant( const fc::flat_set<T>& var, variant& vo );
|
|
template<typename T>
|
|
void from_variant( const variant& var, fc::flat_set<T>& vo );
|
|
|
|
template<typename T>
|
|
void to_variant( const std::set<T>& var, variant& vo );
|
|
template<typename T>
|
|
void from_variant( const variant& var, std::set<T>& vo );
|
|
|
|
void to_variant( const time_point& var, variant& vo );
|
|
void from_variant( const variant& var, time_point& vo );
|
|
|
|
void to_variant( const time_point_sec& var, variant& vo );
|
|
void from_variant( const variant& var, time_point_sec& vo );
|
|
|
|
void to_variant( const microseconds& input_microseconds, variant& output_variant );
|
|
void from_variant( const variant& input_variant, microseconds& output_microseconds );
|
|
|
|
#ifdef __APPLE__
|
|
void to_variant( size_t s, variant& v );
|
|
#elif !defined(_MSC_VER)
|
|
void to_variant( long long int s, variant& v );
|
|
void to_variant( unsigned long long int s, variant& v );
|
|
#endif
|
|
void to_variant( const std::string& s, variant& v );
|
|
|
|
template<typename T>
|
|
void to_variant( const std::shared_ptr<T>& var, variant& vo );
|
|
|
|
template<typename T>
|
|
void from_variant( const variant& var, std::shared_ptr<T>& vo );
|
|
|
|
typedef std::vector<variant> variants;
|
|
template<typename A, typename B>
|
|
void to_variant( const std::pair<A,B>& t, variant& v );
|
|
template<typename A, typename B>
|
|
void from_variant( const variant& v, std::pair<A,B>& p );
|
|
|
|
|
|
|
|
/**
|
|
* @brief stores null, int64, uint64, double, bool, string, std::vector<variant>,
|
|
* and variant_object's.
|
|
*
|
|
* variant's allocate everything but strings, arrays, and objects on the
|
|
* stack and are 'move aware' for values allcoated on the heap.
|
|
*
|
|
* Memory usage on 64 bit systems is 16 bytes and 12 bytes on 32 bit systems.
|
|
*/
|
|
class variant
|
|
{
|
|
public:
|
|
enum type_id
|
|
{
|
|
null_type = 0,
|
|
int64_type = 1,
|
|
uint64_type = 2,
|
|
double_type = 3,
|
|
bool_type = 4,
|
|
string_type = 5,
|
|
array_type = 6,
|
|
object_type = 7,
|
|
blob_type = 8
|
|
};
|
|
|
|
/// Constructs a null_type variant
|
|
variant();
|
|
/// Constructs a null_type variant
|
|
variant( nullptr_t );
|
|
|
|
/// @param str - UTF8 string
|
|
variant( const char* str );
|
|
variant( char* str );
|
|
variant( wchar_t* str );
|
|
variant( const wchar_t* str );
|
|
variant( float val );
|
|
variant( uint8_t val );
|
|
variant( int8_t val );
|
|
variant( uint16_t val );
|
|
variant( int16_t val );
|
|
variant( uint32_t val );
|
|
variant( int32_t val );
|
|
variant( uint64_t val );
|
|
variant( int64_t val );
|
|
variant( double val );
|
|
variant( bool val );
|
|
variant( blob val );
|
|
variant( fc::string val );
|
|
variant( variant_object );
|
|
variant( mutable_variant_object );
|
|
variant( variants );
|
|
variant( const variant& );
|
|
variant( variant&& );
|
|
~variant();
|
|
|
|
/**
|
|
* Read-only access to the content of the variant.
|
|
*/
|
|
class visitor
|
|
{
|
|
public:
|
|
virtual ~visitor(){}
|
|
/// handles null_type variants
|
|
virtual void handle()const = 0;
|
|
virtual void handle( const int64_t& v )const = 0;
|
|
virtual void handle( const uint64_t& v )const = 0;
|
|
virtual void handle( const double& v )const = 0;
|
|
virtual void handle( const bool& v )const = 0;
|
|
virtual void handle( const string& v )const = 0;
|
|
virtual void handle( const variant_object& v)const = 0;
|
|
virtual void handle( const variants& v)const = 0;
|
|
};
|
|
|
|
void visit( const visitor& v )const;
|
|
|
|
type_id get_type()const;
|
|
|
|
bool is_null()const;
|
|
bool is_string()const;
|
|
bool is_bool()const;
|
|
bool is_int64()const;
|
|
bool is_uint64()const;
|
|
bool is_double()const;
|
|
bool is_object()const;
|
|
bool is_array()const;
|
|
bool is_blob()const;
|
|
/**
|
|
* int64, uint64, double,bool
|
|
*/
|
|
bool is_numeric()const;
|
|
/**
|
|
* int64, uint64, bool
|
|
*/
|
|
bool is_integer()const;
|
|
|
|
int64_t as_int64()const;
|
|
uint64_t as_uint64()const;
|
|
bool as_bool()const;
|
|
double as_double()const;
|
|
|
|
blob& get_blob();
|
|
const blob& get_blob()const;
|
|
blob as_blob()const;
|
|
|
|
/** Convert's double, ints, bools, etc to a string
|
|
* @throw if get_type() == array_type | get_type() == object_type
|
|
*/
|
|
string as_string()const;
|
|
|
|
/// @pre get_type() == string_type
|
|
const string& get_string()const;
|
|
|
|
/// @throw if get_type() != array_type | null_type
|
|
variants& get_array();
|
|
|
|
/// @throw if get_type() != array_type
|
|
const variants& get_array()const;
|
|
|
|
/// @throw if get_type() != object_type | null_type
|
|
variant_object& get_object();
|
|
|
|
/// @throw if get_type() != object_type
|
|
const variant_object& get_object()const;
|
|
|
|
/// @pre is_object()
|
|
const variant& operator[]( const char* )const;
|
|
/// @pre is_array()
|
|
const variant& operator[]( size_t pos )const;
|
|
/// @pre is_array()
|
|
size_t size()const;
|
|
|
|
/**
|
|
* _types that use non-intrusive variant conversion can implement the
|
|
* following method to implement conversion from variant to T.
|
|
*
|
|
* <code>
|
|
* void from_variant( const Variant& var, T& val )
|
|
* </code>
|
|
*
|
|
* The above form is not always convienant, so the this templated
|
|
* method is used to enable conversion from Variants to other
|
|
* types.
|
|
*/
|
|
template<typename T>
|
|
T as()const
|
|
{
|
|
T tmp;
|
|
from_variant( *this, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
variant& operator=( variant&& v );
|
|
variant& operator=( const variant& v );
|
|
|
|
template<typename T>
|
|
variant& operator=( T&& v )
|
|
{
|
|
return *this = variant( fc::forward<T>(v) );
|
|
}
|
|
|
|
template<typename T>
|
|
variant( const optional<T>& v )
|
|
{
|
|
memset( this, 0, sizeof(*this) );
|
|
if( v.valid() ) *this = variant(*v);
|
|
}
|
|
|
|
template<typename T>
|
|
explicit variant( const T& val );
|
|
|
|
|
|
void clear();
|
|
private:
|
|
void init();
|
|
double _data; ///< Alligned according to double requirements
|
|
char _type[sizeof(void*)]; ///< pad to void* size
|
|
};
|
|
typedef optional<variant> ovariant;
|
|
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, string& vo );
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, variants& vo );
|
|
void from_variant( const variant& var, variant& vo );
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, int64_t& vo );
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, uint64_t& vo );
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, bool& vo );
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, double& vo );
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, float& vo );
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, int32_t& vo );
|
|
/** @ingroup Serializable */
|
|
void from_variant( const variant& var, uint32_t& vo );
|
|
/** @ingroup Serializable */
|
|
template<typename T>
|
|
void from_variant( const variant& var, optional<T>& vo )
|
|
{
|
|
if( var.is_null() ) vo = optional<T>();
|
|
else
|
|
{
|
|
vo = T();
|
|
from_variant( var, *vo );
|
|
}
|
|
}
|
|
template<typename T>
|
|
void to_variant( const std::unordered_set<T>& var, variant& vo )
|
|
{
|
|
std::vector<variant> vars(var.size());
|
|
size_t i = 0;
|
|
for( auto itr = var.begin(); itr != var.end(); ++itr, ++i )
|
|
vars[i] = variant(*itr);
|
|
vo = vars;
|
|
}
|
|
template<typename T>
|
|
void from_variant( const variant& var, std::unordered_set<T>& vo )
|
|
{
|
|
const variants& vars = var.get_array();
|
|
vo.clear();
|
|
vo.reserve( vars.size() );
|
|
for( auto itr = vars.begin(); itr != vars.end(); ++itr )
|
|
vo.insert( itr->as<T>() );
|
|
}
|
|
|
|
|
|
template<typename K, typename T>
|
|
void to_variant( const std::unordered_map<K, T>& var, variant& vo )
|
|
{
|
|
std::vector< variant > vars(var.size());
|
|
size_t i = 0;
|
|
for( auto itr = var.begin(); itr != var.end(); ++itr, ++i )
|
|
vars[i] = fc::variant(*itr);
|
|
vo = vars;
|
|
}
|
|
template<typename K, typename T>
|
|
void from_variant( const variant& var, std::unordered_map<K, T>& vo )
|
|
{
|
|
const variants& vars = var.get_array();
|
|
vo.clear();
|
|
for( auto itr = vars.begin(); itr != vars.end(); ++itr )
|
|
vo.insert( itr->as< std::pair<K,T> >() );
|
|
|
|
}
|
|
template<typename K, typename T>
|
|
void to_variant( const std::map<K, T>& var, variant& vo )
|
|
{
|
|
std::vector< variant > vars(var.size());
|
|
size_t i = 0;
|
|
for( auto itr = var.begin(); itr != var.end(); ++itr, ++i )
|
|
vars[i] = fc::variant(*itr);
|
|
vo = vars;
|
|
}
|
|
template<typename K, typename T>
|
|
void from_variant( const variant& var, std::map<K, T>& vo )
|
|
{
|
|
const variants& vars = var.get_array();
|
|
vo.clear();
|
|
for( auto itr = vars.begin(); itr != vars.end(); ++itr )
|
|
vo.insert( itr->as< std::pair<K,T> >() );
|
|
}
|
|
|
|
template<typename K, typename T>
|
|
void to_variant( const std::multimap<K, T>& var, variant& vo )
|
|
{
|
|
std::vector< variant > vars(var.size());
|
|
size_t i = 0;
|
|
for( auto itr = var.begin(); itr != var.end(); ++itr, ++i )
|
|
vars[i] = fc::variant(*itr);
|
|
vo = vars;
|
|
}
|
|
template<typename K, typename T>
|
|
void from_variant( const variant& var, std::multimap<K, T>& vo )
|
|
{
|
|
const variants& vars = var.get_array();
|
|
vo.clear();
|
|
for( auto itr = vars.begin(); itr != vars.end(); ++itr )
|
|
vo.insert( itr->as< std::pair<K,T> >() );
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
void to_variant( const std::set<T>& var, variant& vo )
|
|
{
|
|
std::vector<variant> vars(var.size());
|
|
size_t i = 0;
|
|
for( auto itr = var.begin(); itr != var.end(); ++itr, ++i )
|
|
vars[i] = variant(*itr);
|
|
vo = vars;
|
|
}
|
|
template<typename T>
|
|
void from_variant( const variant& var, std::set<T>& vo )
|
|
{
|
|
const variants& vars = var.get_array();
|
|
vo.clear();
|
|
//vo.reserve( vars.size() );
|
|
for( auto itr = vars.begin(); itr != vars.end(); ++itr )
|
|
vo.insert( itr->as<T>() );
|
|
}
|
|
|
|
/** @ingroup Serializable */
|
|
template<typename T>
|
|
void from_variant( const variant& var, std::vector<T>& tmp )
|
|
{
|
|
const variants& vars = var.get_array();
|
|
tmp.clear();
|
|
tmp.reserve( vars.size() );
|
|
for( auto itr = vars.begin(); itr != vars.end(); ++itr )
|
|
tmp.push_back( itr->as<T>() );
|
|
}
|
|
|
|
/** @ingroup Serializable */
|
|
template<typename T>
|
|
void to_variant( const std::vector<T>& t, variant& v )
|
|
{
|
|
std::vector<variant> vars(t.size());
|
|
for( size_t i = 0; i < t.size(); ++i )
|
|
vars[i] = variant(t[i]);
|
|
v = vars;
|
|
}
|
|
/** @ingroup Serializable */
|
|
template<typename A, typename B>
|
|
void to_variant( const std::pair<A,B>& t, variant& v )
|
|
{
|
|
std::vector<variant> vars(2);
|
|
vars[0] = variant(t.first);
|
|
vars[1] = variant(t.second);
|
|
v = vars;
|
|
}
|
|
template<typename A, typename B>
|
|
void from_variant( const variant& v, std::pair<A,B>& p )
|
|
{
|
|
const variants& vars = v.get_array();
|
|
if( vars.size() > 0 )
|
|
p.first = vars[0].as<A>();
|
|
if( vars.size() > 1 )
|
|
p.second = vars[1].as<B>();
|
|
}
|
|
|
|
|
|
template<typename T>
|
|
variant::variant( const T& val )
|
|
{
|
|
memset( this, 0, sizeof(*this) );
|
|
to_variant( val, *this );
|
|
}
|
|
#ifdef __APPLE__
|
|
inline void to_variant( size_t s, variant& v ) { v = variant(uint64_t(s)); }
|
|
#endif
|
|
template<typename T>
|
|
void to_variant( const std::shared_ptr<T>& var, variant& vo )
|
|
{
|
|
if( var ) to_variant( *var, vo );
|
|
else vo = nullptr;
|
|
}
|
|
|
|
template<typename T>
|
|
void from_variant( const variant& var, std::shared_ptr<T>& vo )
|
|
{
|
|
if( var.is_null() ) vo = nullptr;
|
|
else if( vo ) from_variant( var, *vo );
|
|
else {
|
|
vo = std::make_shared<T>();
|
|
from_variant( var, *vo );
|
|
}
|
|
}
|
|
|
|
struct from_static_variant
|
|
{
|
|
variant& var;
|
|
from_static_variant( variant& dv ):var(dv){}
|
|
|
|
typedef void result_type;
|
|
template<typename T> void operator()( const T& v )
|
|
{
|
|
to_variant( v, var );
|
|
}
|
|
};
|
|
|
|
struct to_static_variant
|
|
{
|
|
const variant& var;
|
|
to_static_variant( const variant& dv ):var(dv){}
|
|
|
|
typedef void result_type;
|
|
template<typename T> void operator()( T& v )
|
|
{
|
|
to_variant( var, v );
|
|
}
|
|
};
|
|
|
|
|
|
template<typename... T> void to_variant( const fc::static_variant<T...>& s, fc::variant& v )
|
|
{
|
|
variant tmp;
|
|
variants vars(2);
|
|
vars[0] = s.which();
|
|
s.visit( from_static_variant(vars[1]) );
|
|
v = std::move(vars);
|
|
}
|
|
template<typename... T> void from_variant( const fc::variant& v, fc::static_variant<T...>& s )
|
|
{
|
|
auto ar = v.get_array();
|
|
if( ar.size() ) return;
|
|
s.set_which( ar[0].as_uint64() );
|
|
if( ar.size() < 1 ) return;
|
|
s.visit( to_static_variant(ar[1]) );
|
|
}
|
|
|
|
template<typename T>
|
|
void to_variant( const safe<T>& s, variant& v ) { v = s.value; }
|
|
|
|
template<typename T>
|
|
void from_variant( const variant& v, safe<T>& s ) { s.value = v.as_uint64(); }
|
|
|
|
variant operator + ( const variant& a, const variant& b );
|
|
variant operator - ( const variant& a, const variant& b );
|
|
variant operator * ( const variant& a, const variant& b );
|
|
variant operator / ( const variant& a, const variant& b );
|
|
variant operator == ( const variant& a, const variant& b );
|
|
variant operator != ( const variant& a, const variant& b );
|
|
variant operator < ( const variant& a, const variant& b );
|
|
variant operator > ( const variant& a, const variant& b );
|
|
variant operator ! ( const variant& a );
|
|
} // namespace fc
|
|
|
|
#include <fc/reflect/reflect.hpp>
|
|
FC_REFLECT_TYPENAME( fc::variant )
|
|
FC_REFLECT_ENUM( fc::variant::type_id, (null_type)(int64_type)(uint64_type)(double_type)(bool_type)(string_type)(array_type)(object_type)(blob_type) )
|
|
FC_REFLECT( fc::blob, (data) );
|