Skip to content

Instantly share code, notes, and snippets.

@Bananattack
Created October 10, 2016 02:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bananattack/7769e48269fb517aed0bcaccd993c2df to your computer and use it in GitHub Desktop.
Save Bananattack/7769e48269fb517aed0bcaccd993c2df to your computer and use it in GitHub Desktop.
class binding + deserialization
#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
#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