pclass: Implement casters between primitives and json object

This commit is contained in:
Joshua Scott 2018-12-15 16:51:45 +00:00
parent 20f74ee004
commit def6549bcb
10 changed files with 20730 additions and 15 deletions

View File

@ -5,6 +5,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake)
set(RAPIDXML_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/third_party)
add_library(RapidXML INTERFACE)
target_include_directories(RapidXML INTERFACE ${RAPIDXML_INCLUDE_DIR})
set(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/third_party)
add_library(JSON INTERFACE)
target_include_directories(JSON INTERFACE ${JSON_INCLUDE_DIR})
find_package(ZLIB REQUIRED)
include_directories(${ZLIB_INCLUDE_DIRS})
@ -19,7 +22,7 @@ target_include_directories(${PROJECT_NAME}
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
target_link_libraries(${PROJECT_NAME} RapidXML ${ZLIB_LIBRARIES})
target_link_libraries(${PROJECT_NAME} RapidXML JSON ${ZLIB_LIBRARIES})
add_subdirectory("src/dml")
add_subdirectory("src/pclass")

337
include/ki/pclass/Casters.h Normal file
View File

@ -0,0 +1,337 @@
#pragma once
#include <type_traits>
#include <sstream>
#include <json.hpp>
#include "ki/pclass/Value.h"
#include "ki/pclass/types/EnumType.h"
namespace ki
{
namespace pclass
{
namespace detail
{
/**
* A utility to call ValueCaster::declare<SrcT, DestT> with
* bi<N> and bui<N> as the destination type.
*
* N is automatically decremented until it reaches 0.
*/
template <typename T, int N = 7>
struct bit_integer_declarer
{
static void declare()
{
bit_integer_declarer<T, N - 1>::declare();
ValueCaster::declare<T, bi<N>>();
ValueCaster::declare<T, bui<N>>();
}
};
/**
* Specialization for bit_integer_declarer.
* Stop decrementing N when it reaches 0.
*/
template <typename T>
struct bit_integer_declarer<T, 0>
{
static void declare() {}
};
/**
* Determines whether a type can be assigned to a
* nlohmann::json object.
*/
template <typename SrcT, typename Enable = void>
struct is_json_assignable : std::false_type {};
/**
* All fundamental types can be assigned to a json object.
*/
template <typename SrcT>
struct is_json_assignable<
SrcT,
typename std::enable_if<std::is_fundamental<SrcT>::value>::type
> : std::true_type {};
/**
* std::string can be assigned to a json object.
*/
template <>
struct is_json_assignable<std::string> : std::true_type {};
/**
* value_caster specialization for the generic case of casting
* any json-assignable value to a json object.
*/
template <typename SrcT>
struct value_caster<
SrcT, nlohmann::json,
typename std::enable_if<is_json_assignable<SrcT>::value>::type
>
: value_caster_impl<SrcT, nlohmann::json>
{
Value cast(const Value &value) const override
{
const nlohmann::json j = value.get<SrcT>();
return Value::make_value<nlohmann::json>(j);
}
};
/**
* value_caster specialization for casting bi<N> and bui<N>
* to a json object.
*/
template <int N, bool Unsigned>
struct value_caster<
BitInteger<N, Unsigned>, nlohmann::json
>
: value_caster_impl<BitInteger<N, Unsigned>, nlohmann::json>
{
using type = typename std::conditional<
Unsigned,
typename bits<N>::uint_type,
typename bits<N>::int_type
>::type;
Value cast(const Value &value) const override
{
const nlohmann::json j = static_cast<type>(
value.get<BitInteger<N, Unsigned>>()
);
return Value::make_value<nlohmann::json>(j);
}
};
/**
* value_caster specialization for casting enums to a json object.
*/
template <typename SrcT>
struct value_caster<
SrcT, nlohmann::json,
typename std::enable_if<std::is_enum<SrcT>::value>::type
>
: value_caster_impl<SrcT, nlohmann::json>
{
using underlying_type = typename std::underlying_type<SrcT>::type;
Value cast(const Value &value) const override
{
const auto underlying_value =
static_cast<underlying_type>(value.get<SrcT>());
const nlohmann::json j = underlying_value;
return Value::make_value<nlohmann::json>(j);
}
};
/**
* value_caster specialization for casting enums to bit integer types.
*/
template <
typename SrcT,
int N, bool Unsigned
>
struct value_caster<
SrcT, BitInteger<N, Unsigned>,
typename std::enable_if<std::is_enum<SrcT>::value>::type
>
: value_caster_impl<SrcT, nlohmann::json>
{
using underlying_type = typename std::underlying_type<SrcT>::type;
Value cast(const Value &value) const override
{
const auto underlying_value =
static_cast<underlying_type>(value.get<SrcT>());
const auto bit_value = BitInteger<N, Unsigned>(underlying_value);
return Value::make_value<BitInteger<N, Unsigned>>(bit_value);
}
};
template <typename T, typename Enable = void>
struct string_cast_t
{
using type = T;
};
/**
* Writing a int8_t or uint8_t value to a stream will
* write a ASCII character, rather than an integer, so
* we cast to int16_t/uint16_t before writing.
*/
template <>
struct string_cast_t<int8_t>
{
using type = int16_t;
};
template <>
struct string_cast_t<uint8_t>
{
using type = uint16_t;
};
/**
* Enums should be written as 32-bit integers.
*/
template <typename T>
struct string_cast_t<
T,
typename std::enable_if<std::is_enum<T>::value>::type
>
{
using type = enum_value_t;
};
/**
* Caster implementation for casting any type to string
* via std::ostringstream.
*/
template <typename SrcT>
struct value_caster<SrcT, std::string>
: value_caster_impl<SrcT, std::string>
{
Value cast(const Value &value) const override
{
std::ostringstream oss;
auto casted_value = static_cast<
typename string_cast_t<SrcT>::type
>(value.get<SrcT>());
oss << casted_value;
return Value::make_value<std::string>(oss.str());
}
};
/**
* Utility class used by TypeSystem to declare casters for each
* primitive type.
*/
template <typename T, typename Enable = void>
struct caster_declarer
{
static void declare() {}
};
/**
* caster_declarer specialization for all integral types.
* Integers can cast to other integer types, as well as floating point
* integers, std::string and nlohmann::json.
*/
template <typename T>
struct caster_declarer<
T,
typename std::enable_if<is_integral<T>::value>::type
>
{
static void declare()
{
ValueCaster::declare<T, bool>();
bit_integer_declarer<T>::declare();
ValueCaster::declare<T, int8_t>();
ValueCaster::declare<T, int16_t>();
ValueCaster::declare<T, bi<24>>();
ValueCaster::declare<T, int32_t>();
ValueCaster::declare<T, int64_t>();
ValueCaster::declare<T, uint8_t>();
ValueCaster::declare<T, uint16_t>();
ValueCaster::declare<T, bui<24>>();
ValueCaster::declare<T, uint32_t>();
ValueCaster::declare<T, uint64_t>();
ValueCaster::declare<T, float>();
ValueCaster::declare<T, double>();
ValueCaster::declare<T, std::string>();
ValueCaster::declare<T, nlohmann::json>();
}
};
/**
* caster_declarer specialization for all floating point types.
* Floating point integers can cast to all integer types, as well as
* std::string and nlohmann::json.
*/
template <typename T>
struct caster_declarer<
T,
typename std::enable_if<std::is_floating_point<T>::value>::type
>
{
static void declare()
{
ValueCaster::declare<T, bool>();
bit_integer_declarer<T, 7>::declare();
ValueCaster::declare<T, int8_t>();
ValueCaster::declare<T, int16_t>();
ValueCaster::declare<T, bi<24>>();
ValueCaster::declare<T, int32_t>();
ValueCaster::declare<T, int64_t>();
ValueCaster::declare<T, uint8_t>();
ValueCaster::declare<T, uint16_t>();
ValueCaster::declare<T, bui<24>>();
ValueCaster::declare<T, uint32_t>();
ValueCaster::declare<T, uint64_t>();
ValueCaster::declare<T, float>();
ValueCaster::declare<T, double>();
ValueCaster::declare<T, std::string>();
ValueCaster::declare<T, nlohmann::json>();
}
};
/**
* caster_declarer specialization for enum types.
* Enums can be cast to any integer type, as well as std::string,
* and nlohmann::json.
*/
template <typename T>
struct caster_declarer<
T,
typename std::enable_if<std::is_enum<T>::value>::type
>
{
static void declare()
{
ValueCaster::declare<T, bool>();
bit_integer_declarer<T, 7>::declare();
ValueCaster::declare<T, int8_t>();
ValueCaster::declare<T, int16_t>();
ValueCaster::declare<T, bi<24>>();
ValueCaster::declare<T, int32_t>();
ValueCaster::declare<T, int64_t>();
ValueCaster::declare<T, uint8_t>();
ValueCaster::declare<T, uint16_t>();
ValueCaster::declare<T, bui<24>>();
ValueCaster::declare<T, uint32_t>();
ValueCaster::declare<T, uint64_t>();
ValueCaster::declare<T, std::string>();
ValueCaster::declare<T, nlohmann::json>();
}
};
/**
* caster_declarer specialization for string.
* Strings can be cast to UTF-16 strings, and json objects.
*/
template <>
struct caster_declarer<std::string>
{
static void declare()
{
// TODO: Casting string to u16string
ValueCaster::declare<std::string, nlohmann::json>();
}
};
/**
* caster_declarer specialization for UTF-16 strings.
* UTF-16 strings can be cast to strings, and json objects.
*/
template <>
struct caster_declarer<std::u16string>
{
static void declare()
{
// TODO: Casting u16string to string
ValueCaster::declare<std::u16string, nlohmann::json>();
}
};
}
}
}

View File

@ -36,6 +36,12 @@ namespace pclass
return ValueT(prop.m_value);
}
static Value get_value(const StaticProperty<ValueT> &prop)
{
// Return a reference to the property's value
return Value::make_reference<ValueT>(prop.m_value);
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
// ValueT does not derive from PropertyClass, and so, this property is not
@ -80,6 +86,8 @@ namespace pclass
>::type
>
{
using nonpointer_type = typename std::remove_pointer<ValueT>::type;
static ValueT construct(const Type &type)
{
// The default value of pointers is null
@ -93,6 +101,11 @@ namespace pclass
return prop.m_value;
}
static Value get_value(const StaticProperty<ValueT> &prop)
{
return Value::make_reference<nonpointer_type>(*prop.m_value);
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
// ValueT does not derive from PropertyClass, and so, this property is not
@ -140,6 +153,8 @@ namespace pclass
>::type
>
{
using nonpointer_type = typename std::remove_pointer<ValueT>::type;
static ValueT construct(const Type &type)
{
// The default value of pointers is null
@ -153,6 +168,11 @@ namespace pclass
return prop.m_value;
}
static Value get_value(const StaticProperty<ValueT> &prop)
{
return Value::make_reference<nonpointer_type>(*prop.m_value);
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
// ValueT does derive from PropertyClass, and we have a pointer to an instance
@ -215,6 +235,11 @@ namespace pclass
delete value_ptr;
return value;
}
static Value get_value(const StaticProperty<ValueT> &prop)
{
return Value::make_reference<ValueT>(prop.m_value);
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
@ -313,6 +338,11 @@ namespace pclass
return value_object_helper<ValueT>::copy(prop);
}
static Value get_value(const StaticProperty<ValueT> &prop)
{
return value_object_helper<ValueT>::get_value(prop);
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
return value_object_helper<ValueT>::get_object(prop);
@ -381,7 +411,7 @@ namespace pclass
Value get_value() const override
{
return Value::make_reference(m_value);
return value_helper<ValueT>::get_value(*this);
}
const PropertyClass *get_object() const override

View File

@ -6,6 +6,7 @@
#include "ki/pclass/types/PrimitiveType.h"
#include "ki/pclass/types/ClassType.h"
#include "ki/pclass/types/EnumType.h"
#include "ki/pclass/Casters.h"
namespace ki
{
@ -30,6 +31,7 @@ namespace pclass
template <typename ValueT>
PrimitiveType<ValueT> &define_primitive(const std::string &name)
{
detail::caster_declarer<ValueT>::declare();
auto *type = new PrimitiveType<ValueT>(name, *this);
define_type(std::unique_ptr<Type>(
dynamic_cast<Type *>(type)
@ -50,9 +52,12 @@ namespace pclass
return define_class<ClassT>(name, &base_class);
}
EnumType &define_enum(const std::string &name);
template <typename EnumT>
CppEnumType<EnumT> &define_enum(const std::string &name)
{
detail::caster_declarer<EnumT>::declare();
auto *type = new CppEnumType<EnumT>(name, *this);
define_type(std::unique_ptr<Type>(
dynamic_cast<Type *>(type)
@ -83,6 +88,8 @@ namespace pclass
ClassType<ClassT> &define_class(
const std::string &name, const Type *base_class)
{
detail::caster_declarer<ClassT>::declare();
// If the caller does not specify a base class, automatically make
// ki::pclass::PropertyClass the base class (if it has been defined)
if (base_class == nullptr && has_type("class PropertyClass"))

View File

@ -15,7 +15,10 @@ namespace pclass
namespace detail
{
template <typename SrcT, typename DestT>
template <
typename SrcT, typename DestT,
typename SrcEnable = void, typename DestEnable = void
>
struct value_caster;
/**
@ -160,7 +163,7 @@ namespace pclass
* A static lookup used to find appropriate casters at runtime.
* Contains SrcT -> Caster elements.
*/
static std::unordered_map<std::size_t, ValueCaster *> s_caster_lookup;
static std::unordered_map<std::size_t, ValueCaster *> *s_caster_lookup;
const std::type_info *m_src_type;
std::unordered_map<std::size_t, detail::value_caster_base *> m_casts;
@ -175,11 +178,14 @@ namespace pclass
template <typename SrcT>
static ValueCaster &get()
{
if (!s_caster_lookup)
s_caster_lookup = new std::unordered_map<std::size_t, ValueCaster *>();
const auto &src_type = typeid(SrcT);
const auto src_type_hash = src_type.hash_code();
if (s_caster_lookup.find(src_type_hash) == s_caster_lookup.end())
s_caster_lookup[src_type_hash] = new ValueCaster(src_type);
return *s_caster_lookup[src_type_hash];
if (s_caster_lookup->find(src_type_hash) == s_caster_lookup->end())
(*s_caster_lookup)[src_type_hash] = new ValueCaster(src_type);
return *s_caster_lookup->at(src_type_hash);
}
template <typename SrcT, typename DestT>
@ -388,7 +394,10 @@ namespace pclass
/**
* TODO: Documentation
*/
template <typename SrcT, typename DestT>
template <
typename SrcT, typename DestT,
typename SrcEnable, typename DestEnable
>
struct value_caster : value_caster_impl<SrcT, DestT>
{
Value cast(const Value &value) const override

View File

@ -34,6 +34,14 @@ namespace pclass
return ValueT(prop.at(index));
}
static Value get_value(const VectorProperty<ValueT> &prop, const int index)
{
// Ensure index is within bounds
if (index < 0 || index >= prop.size())
throw runtime_error("Index out of bounds.");
return Value::make_reference(prop.at(index));
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop, const int index)
{
// ValueT does not derive from PropertyClass, and so, this property is not
@ -82,6 +90,14 @@ namespace pclass
return prop.at(index);
}
static Value get_value(const VectorProperty<ValueT> &prop, const int index)
{
// Ensure index is within bounds
if (index < 0 || index >= prop.size())
throw runtime_error("Index out of bounds.");
return Value::make_reference(*prop.at(index));
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop, const int index)
{
// ValueT does not derive from PropertyClass, and so, this property is not
@ -126,6 +142,14 @@ namespace pclass
return prop.at(index);
}
static Value get_value(const VectorProperty<ValueT> &prop, const int index)
{
// Ensure index is within bounds
if (index < 0 || index >= prop.size())
throw runtime_error("Index out of bounds.");
return Value::make_reference(*prop.at(index));
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop, const int index)
{
// Ensure index is within bounds
@ -185,6 +209,14 @@ namespace pclass
return value;
}
static Value get_value(const VectorProperty<ValueT> &prop, const int index)
{
// Ensure index is within bounds
if (index < 0 || index >= prop.size())
throw runtime_error("Index out of bounds.");
return Value::make_reference(prop.at(index));
}
static const PropertyClass *get_object(const VectorProperty<ValueT> &prop, const int index)
{
// Ensure index is within bounds
@ -300,6 +332,12 @@ namespace pclass
return vector_value_object_helper<ValueT>::get_object(prop, index);
}
static Value get_value(const VectorProperty<ValueT> &prop,
const int index)
{
return vector_value_object_helper<ValueT>::get_value(prop, index);
}
static void set_object(VectorProperty<ValueT> &prop,
std::unique_ptr<PropertyClass> &object, const int index)
{
@ -364,7 +402,7 @@ namespace pclass
{
if (index < 0 || index >= this->size())
throw runtime_error("Index out of bounds.");
return Value::make_reference(this->at(index));
return vector_value_helper<ValueT>::get_value(*this, index);
}
const PropertyClass *get_object(const int index) const override

View File

@ -1,4 +1,5 @@
#pragma once
#include <cstdint>
#include <string>
namespace ki
@ -7,6 +8,9 @@ namespace pclass
{
namespace detail
{
/**
* primitive_type_helper specialization for string types.
*/
template <
typename _Elem,
typename _Traits,

View File

@ -13,8 +13,8 @@ namespace ki
{
namespace pclass
{
template <int N>
void define_bit_integer_primitive(pclass::TypeSystem &type_system)
template <int N = 7>
void define_bit_integer_primitive(TypeSystem &type_system)
{
define_bit_integer_primitive<N - 1>(type_system);
@ -29,6 +29,10 @@ namespace pclass
type_system.define_primitive<bui<N>>(oss.str());
}
/**
* Specialization for define_bit_integer_primitive.
* Stop decrementing N when it reaches 0.
*/
template <>
void define_bit_integer_primitive<0>(TypeSystem &type_system) {}
@ -58,7 +62,7 @@ namespace pclass
define_primitive<uint64_t>("gid");
// Define bit-integer types
define_bit_integer_primitive<7>(*this);
define_bit_integer_primitive(*this);
define_primitive<bi<24>>("s24");
define_primitive<bui<24>>("u24");
@ -118,6 +122,15 @@ namespace pclass
return *it->second;
}
EnumType &TypeSystem::define_enum(const std::string& name)
{
auto *type = new EnumType(name, *this);
define_type(std::unique_ptr<Type>(
dynamic_cast<Type *>(type)
));
return *type;
}
void TypeSystem::define_type(std::unique_ptr<Type> type)
{
// Does a type with this name already exist?

View File

@ -4,6 +4,9 @@ namespace ki
{
namespace pclass
{
// Initialize the static lookup map
std::unordered_map<std::size_t, ValueCaster *> *ValueCaster::s_caster_lookup = nullptr;
namespace detail
{
Value value_caster_base::cast(const Value &v) const
@ -109,8 +112,5 @@ namespace pclass
m_deallocator.deallocate(m_value_ptr);
m_value_ptr = nullptr;
}
// Initialize the static lookup map
std::unordered_map<std::size_t, ValueCaster *> ValueCaster::s_caster_lookup = {};
}
}

20274
third_party/json.hpp vendored Normal file

File diff suppressed because it is too large Load Diff