Created
October 10, 2016 02:43
-
-
Save Bananattack/7769e48269fb517aed0bcaccd993c2df to your computer and use it in GitHub Desktop.
class binding + deserialization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CLASS_REGISTRY_H | |
#define CLASS_REGISTRY_H | |
// Uses sajson: https://github.com/chadaustin/sajson | |
#include "sajson.h" | |
#include <unordered_map> | |
#include <memory> | |
#include <limits> | |
#include <functional> | |
namespace dew { | |
template<typename Setter> | |
struct SetterInvoker; | |
template<typename SetterClass, typename SetterArgument> | |
struct SetterInvoker<void(SetterClass::*)(SetterArgument)> { | |
typedef void(SetterClass::*MethodType)(SetterArgument); | |
typedef SetterArgument ArgumentType; | |
template<typename Class> | |
static void invoke(Class& self, const MethodType& setter, const ArgumentType& argument) { | |
(self.*setter)(argument); | |
} | |
}; | |
template<typename SetterClass, typename SetterArgument> | |
struct SetterInvoker<void(*)(SetterClass&, SetterArgument)> { | |
typedef void(*MethodType)(SetterClass&, SetterArgument); | |
typedef SetterArgument ArgumentType; | |
template<typename Class> | |
static void invoke(Class& self, const MethodType& setter, const ArgumentType& argument) { | |
setter(self, argument); | |
} | |
}; | |
class PropertyReader { | |
public: | |
virtual ~PropertyReader() {} | |
virtual bool read(const sajson::value& value, void* data) = 0; | |
}; | |
template<typename Class, typename Setter, typename Primitive> | |
class IntegerPropertyReader : public PropertyReader { | |
static_assert(sizeof(Primitive) <= sizeof(int), ""); | |
public: | |
IntegerPropertyReader(const Setter& setter) : setter(setter) {} | |
virtual bool read(const sajson::value& value, void* data) override { | |
Class* obj = static_cast<Class*>(data); | |
int v = 0; | |
bool valid = false; | |
switch(value.get_type()) { | |
case sajson::TYPE_INTEGER: { | |
v = value.get_integer_value(); | |
valid = true; | |
break; | |
} | |
case sajson::TYPE_DOUBLE: { | |
v = static_cast<Primitive>(value.get_double_value()); | |
valid = true; | |
break; | |
} | |
} | |
if(valid) { | |
if(std::numeric_limits<Primitive>::is_signed) { | |
valid = std::numeric_limits<Primitive>::min() <= v && v <= std::numeric_limits<Primitive>::max(); | |
} else { | |
valid = sizeof(Primitive) < sizeof(int) && 0 <= v && v <= static_cast<int>(std::numeric_limits<Primitive>::max()) | |
|| sizeof(Primitive) == sizeof(int) && 0 <= v && static_cast<Primitive>(v) <= std::numeric_limits<Primitive>::max(); | |
} | |
} | |
if(valid) { | |
SetterInvoker<Setter>::template invoke<Class>(*obj, setter, static_cast<Primitive>(v)); | |
return true; | |
} | |
return false; | |
} | |
private: | |
Setter setter; | |
}; | |
template<typename Class, typename Setter> | |
class StringPropertyReader : public PropertyReader { | |
public: | |
StringPropertyReader(const Setter& setter) : setter(setter) {} | |
virtual bool read(const sajson::value& value, void* data) override { | |
Class* obj = static_cast<Class*>(data); | |
switch(value.get_type()) { | |
case sajson::TYPE_STRING: { | |
SetterInvoker<Setter>::template invoke<Class>(*obj, setter, value.get_string_value().as_string()); | |
return true; | |
} | |
} | |
return false; | |
} | |
private: | |
Setter setter; | |
}; | |
template<typename Class, typename Setter, typename SetterArgument> | |
struct PropertyReaderForSetter; | |
template<typename Class, typename Setter> | |
struct PropertyReaderForSetter<Class, Setter, int8_t> { | |
static std::shared_ptr<PropertyReader> create(const Setter& setter) { | |
return std::make_shared<IntegerPropertyReader<Class, Setter, int8_t>>(setter); | |
} | |
}; | |
template<typename Class, typename Setter> | |
struct PropertyReaderForSetter<Class, Setter, uint8_t> { | |
static std::shared_ptr<PropertyReader> create(const Setter& setter) { | |
return std::make_shared<IntegerPropertyReader<Class, Setter, uint8_t>>(setter); | |
} | |
}; | |
template<typename Class, typename Setter> | |
struct PropertyReaderForSetter<Class, Setter, int16_t> { | |
static std::shared_ptr<PropertyReader> create(const Setter& setter) { | |
return std::make_shared<IntegerPropertyReader<Class, Setter, int16_t>>(setter); | |
} | |
}; | |
template<typename Class, typename Setter> | |
struct PropertyReaderForSetter<Class, Setter, uint16_t> { | |
static std::shared_ptr<PropertyReader> create(const Setter& setter) { | |
return std::make_shared<IntegerPropertyReader<Class, Setter, uint16_t>>(setter); | |
} | |
}; | |
template<typename Class, typename Setter> | |
struct PropertyReaderForSetter<Class, Setter, int32_t> { | |
static std::shared_ptr<PropertyReader> create(const Setter& setter) { | |
return std::make_shared<IntegerPropertyReader<Class, Setter, int32_t>>(setter); | |
} | |
}; | |
template<typename Class, typename Setter> | |
struct PropertyReaderForSetter<Class, Setter, uint32_t> { | |
static std::shared_ptr<PropertyReader> create(const Setter& setter) { | |
return std::make_shared<IntegerPropertyReader<Class, Setter, uint32_t>>(setter); | |
} | |
}; | |
template<typename Class, typename Setter> | |
struct PropertyReaderForSetter<Class, Setter, std::string> { | |
static std::shared_ptr<PropertyReader> create(const Setter& setter) { | |
return std::make_shared<StringPropertyReader<Class, Setter>>(setter); | |
} | |
}; | |
template<typename Class, typename Setter> | |
struct PropertyReaderForSetter<Class, Setter, const std::string&> { | |
static std::shared_ptr<PropertyReader> create(const Setter& setter) { | |
return std::make_shared<StringPropertyReader<Class, Setter>>(setter); | |
} | |
}; | |
template<typename Base> | |
class ClassReflector { | |
public: | |
virtual ~ClassReflector() {} | |
virtual std::shared_ptr<Base> create() const = 0; | |
virtual bool read(const sajson::value& value, void* data) const = 0; | |
}; | |
template<typename Base, typename Derived> | |
class DerivedClassReflector : public ClassReflector<Base> { | |
public: | |
virtual std::shared_ptr<Base> create() const override { | |
return std::make_shared<Derived>(); | |
} | |
virtual bool read(const sajson::value& value, void* data) const override { | |
switch(value.get_type()) { | |
case sajson::TYPE_OBJECT: { | |
for(const auto& it : properties) { | |
std::size_t index = value.find_object_key(sajson::string(it.first.data(), it.first.length())); | |
if(index != value.get_length()) { | |
it.second->read(value.get_object_value(index), data); | |
} else { | |
return false; | |
} | |
} | |
return true; | |
} | |
default: { | |
return false; | |
} | |
} | |
return false; | |
} | |
std::unordered_map<std::string, std::shared_ptr<PropertyReader>> properties; | |
}; | |
template<typename T> | |
class ClassDeclaration { | |
public: | |
ClassDeclaration() = delete; | |
explicit ClassDeclaration(std::unordered_map<std::string, std::shared_ptr<PropertyReader>>& properties) | |
: properties(properties) { | |
} | |
template<typename Setter> | |
ClassDeclaration<T>& add_property(const std::string& name, const Setter& setter) { | |
properties[name] = PropertyReaderForSetter<T, Setter, SetterInvoker<Setter>::ArgumentType>::create(setter); | |
return *this; | |
} | |
private: | |
std::unordered_map<std::string, std::shared_ptr<PropertyReader>>& properties; | |
}; | |
template<typename Base> | |
class ClassRegistry { | |
public: | |
ClassRegistry() {} | |
template<typename Derived> | |
ClassDeclaration<Derived> add_class(const std::string& name) { | |
auto cls = std::make_shared<DerivedClassReflector<Base, Derived>>(); | |
classes[name] = cls; | |
return ClassDeclaration<Derived>(cls->properties); | |
} | |
bool has_class(const std::string& name) const { | |
auto it = classes.find(name); | |
return it != classes.end(); | |
} | |
template<typename Derived> | |
std::shared_ptr<Derived> create(const std::string& name) const { | |
auto it = classes.find(name); | |
return it != classes.end() | |
? std::static_pointer_cast<Derived>(it->second->create()) | |
: nullptr; | |
} | |
template<typename Derived> | |
std::shared_ptr<Derived> create(const std::string& name, const sajson::value& value) { | |
if(auto obj = create<Derived>(name)) { | |
read(name, value, obj.get()); | |
return obj; | |
} | |
return nullptr; | |
} | |
template<typename Derived> | |
std::shared_ptr<Derived> create(const std::string& name, const std::string& source) { | |
if(auto obj = create<Derived>(name)) { | |
read(name, source, obj.get()); | |
return obj; | |
} | |
return nullptr; | |
} | |
template<typename Derived> | |
bool read(const std::string& name, const sajson::value& value, Derived* data) const { | |
auto it = classes.find(name); | |
return it != classes.end() | |
? it->second->read(value, data) | |
: false; | |
} | |
template<typename Derived> | |
bool read(const std::string name, const std::string& source, Derived* data) const { | |
auto document = sajson::parse(sajson::string(source.data(), source.length())); | |
if(document.is_valid()) { | |
auto& root(document.get_root()); | |
return read(name, root, data); | |
} | |
return false; | |
} | |
private: | |
std::unordered_map<std::string, std::shared_ptr<ClassReflector<Base>>> classes; | |
}; | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include "class_registry.h" | |
class Object { | |
public: | |
virtual ~Object() {} | |
}; | |
class Item : public Object { | |
public: | |
Item() {} | |
void set_name(const std::string& value) { name = value; } | |
void set_price(int value) { price = value; } | |
std::ostream& write(std::ostream& os) const { | |
os << name << " " << price; | |
return os; | |
} | |
private: | |
std::string name; | |
int price; | |
}; | |
std::ostream& operator<<(std::ostream& os, const Item& item) { | |
return item.write(os); | |
} | |
int main(int argc, char** argv) { | |
// Create a class registry, indicate its base type. | |
dew::ClassRegistry<Object> registry; | |
// Register a type, and bind setter functions. | |
registry.add_class<Item>("Item") | |
.add_property("name", &Item::set_name) | |
.add_property("price", &Item::set_price); | |
// Create a type from a JSON blob. | |
auto item = registry.create<Item>("Item", "{\"name\": \"Potion\", \"price\": 123}"); | |
std::cout << *item << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment