├── Makefile ├── README.markdown ├── base ├── array.hpp ├── array_type.cpp ├── array_type.hpp ├── bag.cpp ├── bag.hpp ├── basic.hpp ├── maybe.hpp ├── maybe_type.cpp └── maybe_type.hpp ├── object ├── child_list.cpp ├── child_list.hpp ├── composite_type.cpp ├── composite_type.hpp ├── object.cpp ├── object.hpp ├── objectptr.hpp ├── reflect.hpp ├── signal.cpp ├── signal.hpp ├── struct_type.cpp ├── struct_type.hpp ├── universe.cpp └── universe.hpp ├── serialization ├── archive.cpp ├── archive.hpp ├── archive_node.cpp ├── archive_node.hpp ├── archive_node_type.hpp ├── deserialize_object.cpp ├── deserialize_object.hpp ├── json_archive.cpp ├── json_archive.hpp └── serialize.hpp ├── test.cpp ├── test ├── Makefile └── maybe_test.cpp └── type ├── attribute.hpp ├── reference_type.cpp ├── reference_type.hpp ├── type.cpp ├── type.hpp ├── type_registry.cpp └── type_registry.hpp /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES = $(wildcard base/*.cpp) $(wildcard serialization/*.cpp) $(wildcard type/*.cpp) $(wildcard object/*.cpp) *.cpp 2 | 3 | CXXFLAGS = -O0 -g -std=c++11 -stdlib=libc++ -Wall -Werror -Wno-unused 4 | 5 | all: 6 | clang++.svn $(CXXFLAGS) $(SOURCES) -I. -o aspect 7 | #clang++ $(CXXFLAGS) *.cpp -S 8 | #clang++ -S -emit-llvm $(CXXFLAGS) *.cpp 9 | #clang++ -S -emit-ast $(CXXFLAGS) *.cpp -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | C++ reflection and serialization framework 2 | ========================================== 3 | 4 | This is a simple library that allows for simple reflection and serialization of C++ types. It is designed for use in video game engines, or other rich environments in which runtime object creation and reflection is useful. 5 | 6 | Features 7 | -------- 8 | 9 | * No meta-object compiler / preprocessing build step. 10 | * Composite types ("aspect oriented programming") with rich interface casts. 11 | * Simple serialization (to JSON at the moment). 12 | * Simple and efficient signal/slot implementation included. 13 | * Type-safe. 14 | * C++11 compliant and extensible -- for instance, serializers for custom types can be easily defined, without modification to those types. 15 | 16 | Planned/pending features 17 | ------------------------ 18 | 19 | * YAML serialization. 20 | * XML serialization. 21 | * Built-in support for serialization of more standard library types. 22 | 23 | Limitations 24 | ----------- 25 | 26 | * Objects must derive from the `Object` type and include the `REFLECT` tag in their definition. 27 | * Requires runtime type information. 28 | * Members of objects that aren't described by a `property(member, name, description)` will not be serialized/deserialized. 29 | * Class reflection/serialization with properties does not currently support multiple inheritance with non-interface (non-abstract) base classes. Use composite objects if needed. A class can derived from multiple base classes, but only one of them may derive from `Object`. 30 | 31 | Examples 32 | ======== 33 | 34 | In foo.hpp: 35 | 36 | 37 | #include "object.hpp" 38 | 39 | class Foo : public Object { 40 | REFLECT; 41 | public: 42 | Foo() : an_integer_property(123) {} 43 | void add_number(float32 n) { a_list_of_floats.push_back(n); } 44 | void say_hi() { std::cout << "Hi!\n"; } 45 | private: 46 | int32 an_integer_property; 47 | Array a_list_of_floats; 48 | }; 49 | 50 | In foo.cpp: 51 | 52 | #include "foo.hpp" 53 | #include "reflect.hpp" 54 | 55 | BEGIN_TYPE_INFO(Foo) 56 | property(&Foo::an_integer_property, "Number", "An integer property."); 57 | property(&Foo::a_list_of_floats, "Float list", "A list of floats."); 58 | 59 | slot(&Foo::say_hi, "Say Hi!", "A slot that says hi."); 60 | END_TYPE_INFO() 61 | 62 | In main.cpp: 63 | 64 | int f() { 65 | // Create a universe for our objects to live in. 66 | TestUniverse universe; 67 | 68 | // Print the type name and all properties. 69 | auto foo_type = get_type(); 70 | std::cout << foo_type->name() << '\n'; // prints "Foo" 71 | for (auto attribute: foo_type->attribute()) { 72 | std::cout << attribute->name() << ": " << attribute->type()->name() << '\n'; 73 | } 74 | std::cout << '\n'; 75 | 76 | // Create a composite type consisting of two Foos. 77 | CompositeType* composite = new CompositeType("FooFoo"); 78 | composite->add_aspect(get_type()); 79 | composite->add_aspect(get_type()); 80 | Object* c = universe.create_object(composite, "My object"); 81 | Foo* f = aspect_cast(c); // get a pointer to the first Foo in c. 82 | f->add_number(7); 83 | f->say_hi(); 84 | 85 | // Serialize the composite as JSON, and write to stdout. 86 | JSONArchive archive; 87 | archive.serialize(c, universe); 88 | archive.write(std::cout); 89 | } 90 | 91 | Output: 92 | 93 | Foo 94 | Number: int32 95 | Float list: float32[] 96 | 97 | { "root": { 98 | "id": "My object", 99 | "aspects": [ 100 | { 101 | "class": "Foo", 102 | "Float list": [7], 103 | "Number": 123 104 | }, 105 | { 106 | "class": "Foo", 107 | "Float list": null, 108 | "Number": 123 109 | } 110 | ], 111 | "class": "FooFoo" 112 | } 113 | } -------------------------------------------------------------------------------- /base/array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ARRAY_HPP_6MM1YKSV 3 | #define ARRAY_HPP_6MM1YKSV 4 | 5 | #include "base/basic.hpp" 6 | 7 | #if defined(USE_STD_VECTOR) 8 | #include 9 | template using Array = std::vector; 10 | #else 11 | 12 | template 13 | class Array; 14 | 15 | template 16 | class Array { 17 | public: 18 | Array() : data_(nullptr), size_(0), alloc_size_(0) {} 19 | Array(const Array& other); 20 | Array(Array&& other); 21 | ~Array(); 22 | Array& operator=(const Array& other); 23 | Array& operator=(Array&& other); 24 | 25 | T& operator[](uint32 idx); 26 | const T& operator[](uint32 idx) const; 27 | 28 | uint32 size() const { return size_; } 29 | void push_back(T element); 30 | void reserve(uint32); 31 | void resize(uint32, T fill = T()); 32 | void clear(bool deallocate = true); 33 | 34 | template 35 | void insert(InputIterator begin, InputIterator end); 36 | 37 | template 38 | void emplace_back(Args... args); 39 | 40 | typedef T value_type; 41 | typedef T* iterator; 42 | typedef const T* const_iterator; 43 | iterator begin() { return data_; } 44 | iterator end() { return data_ + size_; } 45 | const_iterator begin() const { return data_; } 46 | const_iterator end() const { return data_ + size_; } 47 | 48 | iterator erase(iterator); 49 | private: 50 | T* data_; 51 | uint32 size_; 52 | uint32 alloc_size_; 53 | }; 54 | 55 | template 56 | Array::Array(const Array& other) : data_(nullptr), size_(0), alloc_size_(0) { 57 | reserve(other.size()); 58 | insert(other.begin(), other.end()); 59 | } 60 | 61 | template 62 | Array::Array(Array&& other) : data_(other.data_), size_(other.size_), alloc_size_(other.alloc_size_) { 63 | other.data_ = nullptr; 64 | other.size_ = 0; 65 | other.alloc_size_ = 0; 66 | } 67 | 68 | template 69 | Array::~Array() { 70 | clear(true); 71 | } 72 | 73 | template 74 | Array& Array::operator=(const Array& other) { 75 | clear(); 76 | reserve(other.size()); 77 | insert(other.begin(), other.end()); 78 | return *this; 79 | } 80 | 81 | template 82 | Array& Array::operator=(Array&& other) { 83 | data_ = other.data_; 84 | size_ = other.size_; 85 | alloc_size_ = other.alloc_size_; 86 | other.data_ = nullptr; 87 | other.size_ = 0; 88 | other.alloc_size_ = 0; 89 | return *this; 90 | } 91 | 92 | template 93 | T& Array::operator[](uint32 idx) { 94 | ASSERT(idx < size_); 95 | return data_[idx]; 96 | } 97 | 98 | template 99 | const T& Array::operator[](uint32 idx) const { 100 | ASSERT(idx < size_); 101 | return data_[idx]; 102 | } 103 | 104 | template 105 | void Array::push_back(T element) { 106 | reserve(size_+1); 107 | data_[size_++] = element; 108 | } 109 | 110 | template 111 | void Array::reserve(uint32 new_size) { 112 | if (new_size > alloc_size_) { 113 | uint32 req_size = alloc_size_ ? alloc_size_ : 1; 114 | while (req_size < new_size) req_size *= 2; 115 | byte* p = new byte[sizeof(T)*req_size]; 116 | for (uint32 i = 0; i < size_; ++i) { 117 | new(p+sizeof(T)*i) T(std::move(data_[i])); 118 | data_[i].~T(); 119 | } 120 | delete[] reinterpret_cast(data_); 121 | data_ = reinterpret_cast(p); 122 | alloc_size_ = req_size; 123 | } 124 | } 125 | 126 | template 127 | void Array::resize(uint32 new_size, T x) { 128 | reserve(new_size); 129 | while (size_ < new_size) push_back(x); 130 | } 131 | 132 | template 133 | template 134 | void Array::insert(InputIterator begin, InputIterator end) { 135 | reserve(end - begin); 136 | for (auto p = begin; p < end; ++p) { 137 | push_back(*p); 138 | } 139 | } 140 | 141 | template 142 | template 143 | void Array::emplace_back(Args... args) { 144 | reserve(size_+1); 145 | new(data_ + size_) T(std::forward(args)...); 146 | size_++; 147 | } 148 | 149 | template 150 | void Array::clear(bool deallocate) { 151 | for (uint32 i = 0; i < size_; ++i) { 152 | data_[i].~T(); 153 | } 154 | size_ = 0; 155 | if (deallocate) { 156 | delete[] reinterpret_cast(data_); 157 | data_ = nullptr; 158 | alloc_size_ = 0; 159 | } 160 | } 161 | 162 | /*template 163 | Array::iterator Array::erase(Array::iterator it) { 164 | ASSERT(it >= begin() && it < end()); 165 | 166 | }*/ 167 | 168 | #endif // if defined(USE_STD_VECTOR) 169 | 170 | #endif /* end of include guard: ARRAY_HPP_6MM1YKSV */ 171 | -------------------------------------------------------------------------------- /base/array_type.cpp: -------------------------------------------------------------------------------- 1 | #include "base/array_type.hpp" 2 | #include 3 | 4 | std::string build_variable_length_array_type_name(std::string base_name, const Type* element_type) { 5 | std::stringstream ss; 6 | ss << element_type->name() << "[]"; 7 | return ss.str(); 8 | } -------------------------------------------------------------------------------- /base/array_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ARRAY_TYPE_HPP_JIO2A6YN 3 | #define ARRAY_TYPE_HPP_JIO2A6YN 4 | 5 | #include "type/type.hpp" 6 | #include "serialization/archive_node.hpp" 7 | 8 | 9 | struct ArrayType : DerivedType { 10 | public: 11 | const std::string& name() const override { return name_; } 12 | bool is_variable_length() const { return is_variable_length_; } 13 | const Type* element_type() const { return element_type_; } 14 | const Type* type_of_element(size_t idx) const { return element_type(); } 15 | protected: 16 | ArrayType(std::string name, const Type* element_type, bool is_variable_length) : name_(std::move(name)), element_type_(element_type), is_variable_length_(is_variable_length) {} 17 | std::string name_; 18 | const Type* element_type_; 19 | bool is_variable_length_; 20 | }; 21 | 22 | struct FixedArrayType : ArrayType { 23 | public: 24 | FixedArrayType(const Type* element_type, size_t num_elements) : ArrayType(build_fixed_array_type_name(element_type), element_type, false) {} 25 | size_t num_elements() const { return num_elements_; } 26 | size_t offset_of_element(size_t idx) const { return idx * element_type_->size(); } 27 | size_t size() const override { return element_type_->size() * num_elements_; } 28 | 29 | void deserialize(byte* place, const ArchiveNode& node, IUniverse&) const override; 30 | void serialize(const byte* place, ArchiveNode& node, IUniverse&) const override; 31 | protected: 32 | static std::string build_fixed_array_type_name(const Type* element_type); 33 | size_t num_elements_; 34 | }; 35 | 36 | std::string build_variable_length_array_type_name(std::string base_container_name, const Type* element_type); 37 | 38 | template 39 | struct VariableLengthArrayType : TypeFor { 40 | public: 41 | typedef typename Container::value_type ElementType; 42 | VariableLengthArrayType(std::string base_container_name) : TypeFor(build_variable_length_array_type_name(std::move(base_container_name), get_type()), get_type(), true) {} 43 | size_t num_elements() const { return SIZE_T_MAX; } 44 | size_t offset_of_element(size_t idx) const { return idx * this->element_type_->size(); } 45 | 46 | void deserialize(Container& place, const ArchiveNode& node, IUniverse&) const; 47 | void serialize(const Container& place, ArchiveNode& node, IUniverse&) const; 48 | Object* cast(const DerivedType* to, Object* o) const { return nullptr; } 49 | }; 50 | 51 | template 52 | struct BuildTypeInfo> { 53 | static const ArrayType* build() { 54 | static const VariableLengthArrayType> type("Array"); 55 | return &type; 56 | } 57 | }; 58 | 59 | template 60 | void VariableLengthArrayType::deserialize(T& obj, const ArchiveNode& node, IUniverse& universe) const { 61 | if (node.is_array()) { 62 | size_t sz = node.array_size(); 63 | obj.reserve(sz); 64 | for (size_t i = 0; i < sz; ++i) { 65 | ElementType element; 66 | get_type()->deserialize(reinterpret_cast(&element), node[i], universe); 67 | obj.push_back(std::move(element)); 68 | } 69 | } 70 | } 71 | 72 | template 73 | void VariableLengthArrayType::serialize(const T& obj, ArchiveNode& node, IUniverse& universe) const { 74 | for (auto& it: obj) { 75 | ArchiveNode& element = node.array_push(); 76 | get_type()->serialize(reinterpret_cast(&it), element, universe); 77 | } 78 | } 79 | 80 | #endif /* end of include guard: ARRAY_TYPE_HPP_JIO2A6YN */ 81 | -------------------------------------------------------------------------------- /base/bag.cpp: -------------------------------------------------------------------------------- 1 | #include "base/bag.hpp" 2 | 3 | #include 4 | 5 | BagMemoryHandler::PageHeader* BagMemoryHandler::allocate_page() { 6 | static const size_t PageSize = 4096; 7 | size_t sz = element_size_; 8 | if (sz < PageSize) sz = PageSize; 9 | byte* memory = (byte*)mmap(nullptr, sz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 10 | PageHeader* p = new(memory) PageHeader; 11 | p->next = nullptr; 12 | p->begin = memory + sizeof(PageHeader); 13 | p->current = p->begin; 14 | p->end = memory + sz; 15 | return p; 16 | } 17 | 18 | void BagMemoryHandler::deallocate_page(PageHeader* p) { 19 | munmap(p, p->end - p->begin + sizeof(PageHeader)); 20 | } 21 | 22 | byte* BagMemoryHandler::allocate() { 23 | if (free_list_ == nullptr) { 24 | if (current_ == nullptr) { 25 | current_ = allocate_page(); 26 | head_ = current_; 27 | } 28 | 29 | if (current_->end - current_->current < element_size_) { 30 | PageHeader* p = allocate_page(); 31 | current_->next = p; 32 | current_ = p; 33 | } 34 | 35 | byte* memory = current_->current; 36 | current_->current += element_size_; 37 | return memory; 38 | } else { 39 | byte* memory = *free_list_; 40 | free_list_ = (byte**)*memory; 41 | return memory; 42 | } 43 | } 44 | 45 | void BagMemoryHandler::deallocate(byte* ptr) { 46 | *(byte***)ptr = free_list_; 47 | free_list_ = (byte**)ptr; 48 | } 49 | 50 | void BagMemoryHandler::clear() { 51 | PageHeader* p = head_; 52 | while (p != nullptr) { 53 | PageHeader* next = p->next; 54 | deallocate_page(p); 55 | p = next; 56 | } 57 | current_ = nullptr; 58 | head_ = nullptr; 59 | } -------------------------------------------------------------------------------- /base/bag.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef BAG_HPP_LRAVL9CJ 3 | #define BAG_HPP_LRAVL9CJ 4 | 5 | #include "base/array.hpp" 6 | 7 | class BagMemoryHandler { 8 | public: 9 | byte* allocate(); 10 | void deallocate(byte*); 11 | void clear(); 12 | 13 | BagMemoryHandler(size_t element_size) : element_size_(element_size), head_(nullptr), current_(nullptr), free_list_(nullptr) {} 14 | BagMemoryHandler(BagMemoryHandler&& other) : element_size_(other.element_size_), head_(other.head_), current_(other.current_), free_list_(other.free_list_) { 15 | other.head_ = nullptr; other.current_ = nullptr; other.free_list_ = nullptr; 16 | } 17 | ~BagMemoryHandler() { clear(); } 18 | private: 19 | struct PageHeader { 20 | PageHeader* next; 21 | byte* begin; 22 | byte* current; 23 | byte* end; 24 | }; 25 | 26 | PageHeader* allocate_page(); 27 | void deallocate_page(PageHeader* memory); 28 | 29 | const size_t element_size_; 30 | PageHeader* head_; 31 | PageHeader* current_; 32 | byte** free_list_; 33 | }; 34 | 35 | template 36 | class Bag { 37 | public: 38 | static const size_t ElementSize = sizeof(T) < sizeof(byte*) ? sizeof(byte*) : sizeof(T); // make sure there's room for the implicit freelist 39 | 40 | Bag() : memory_(ElementSize) {} 41 | Bag(Bag&& other); 42 | ~Bag() { clear(); } 43 | 44 | template 45 | T* allocate(Args&&...); 46 | void deallocate(T* ptr); 47 | 48 | void clear(); 49 | private: 50 | BagMemoryHandler memory_; 51 | }; 52 | 53 | template 54 | Bag::Bag(Bag&& other) : memory_(std::move(other.memory_)) { 55 | } 56 | 57 | template 58 | template 59 | T* Bag::allocate(Args&&... args) { 60 | byte* p = memory_.allocate(); 61 | return ::new(p) T(std::forward(args)...); 62 | } 63 | 64 | template 65 | void Bag::deallocate(T* ptr) { 66 | // TODO: check that ptr is in bag! 67 | ptr->~T(); 68 | memory_.deallocate(reinterpret_cast(ptr)); 69 | } 70 | 71 | template 72 | void Bag::clear() { 73 | memory_.clear(); 74 | } 75 | 76 | template > 77 | class ContainedBag { 78 | public: 79 | ContainedBag() {} 80 | ContainedBag(ContainedBag&& other) : bag_(std::move(other.bag_)), elements_(std::move(other.elements_)) {} 81 | ~ContainedBag() { clear(); } 82 | 83 | typedef typename Container::iterator iterator; 84 | typedef typename Container::const_iterator const_iterator; 85 | 86 | template 87 | T* allocate(Args&&... args) { 88 | T* ptr = bag_.allocate(std::forward(args)...); 89 | elements_.push_back(ptr); 90 | return ptr; 91 | } 92 | 93 | void deallocate(iterator it) { 94 | bag_.deallocate(*it); 95 | elements_.erase(it); 96 | } 97 | 98 | iterator begin() { return elements_.begin(); } 99 | iterator end() { return elements_.end(); } 100 | const_iterator begin() const { return elements_.begin(); } 101 | const_iterator end() const { return elements_.end(); } 102 | size_t size() const { return elements_.size(); } 103 | 104 | void clear() { 105 | for (auto it: elements_) { 106 | bag_.deallocate(it); 107 | } 108 | elements_.clear(); 109 | bag_.clear(); 110 | } 111 | private: 112 | Bag bag_; 113 | Container elements_; 114 | }; 115 | 116 | #endif /* end of include guard: BAG_HPP_LRAVL9CJ */ 117 | -------------------------------------------------------------------------------- /base/basic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef BASIC_HPP_S0NRU03V 3 | #define BASIC_HPP_S0NRU03V 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef signed char int8; 13 | typedef short int16; 14 | typedef int int32; 15 | typedef long long int int64; 16 | typedef unsigned char uint8; 17 | typedef unsigned short uint16; 18 | typedef unsigned int uint32; 19 | typedef unsigned long long int uint64; 20 | typedef float float32; 21 | typedef double float64; 22 | typedef uint8 byte; 23 | 24 | template 25 | struct HasMember { 26 | typedef char MatchedReturnType; 27 | typedef long UnmatchedReturnType; 28 | 29 | template 30 | static MatchedReturnType f(typename NameGetter::template Check*); 31 | 32 | template 33 | static UnmatchedReturnType f(...); 34 | public: 35 | static const bool Value = (sizeof(f(0)) == sizeof(MatchedReturnType)); 36 | }; 37 | 38 | template 39 | struct RemoveConstRef { 40 | typedef typename std::remove_const::type>::type Type; 41 | }; 42 | 43 | template 44 | struct IsCopyConstructibleNonRef { 45 | static const bool Value = std::is_copy_constructible::Type>::value; 46 | }; 47 | 48 | template 49 | struct IsCopyAssignableNonRef { 50 | static const bool Value = std::is_copy_assignable::Type>::value; 51 | }; 52 | 53 | template 54 | struct IsMoveConstructibleNonRef { 55 | static const bool Value = std::is_move_constructible::Type>::value; 56 | }; 57 | 58 | template 59 | struct IsMoveAssignableNonRef { 60 | static const bool Value = std::is_move_assignable::Type>::value; 61 | }; 62 | 63 | template 64 | void destruct(T* ptr) { 65 | ptr->~T(); 66 | } 67 | 68 | template 69 | auto find_or(Container& container, const Key& key, const DefaultValue& default_value) 70 | -> typename std::common_type::type { 71 | auto it = container.find(key); 72 | if (it != container.end()) return it->second; 73 | return default_value; 74 | } 75 | 76 | #define ASSERT(X) do{ if (!(X)) { fprintf(stderr, "TRAP AT %s:%d (function '%s', expression '%s')\n", __FILE__, __LINE__, __func__, #X); __asm__ __volatile__("int3\n"); } } while(0) 77 | 78 | #endif /* end of include guard: BASIC_HPP_S0NRU03V */ 79 | -------------------------------------------------------------------------------- /base/maybe.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MAYBE_HPP_8R2MUT0P 3 | #define MAYBE_HPP_8R2MUT0P 4 | 5 | #include "base/basic.hpp" 6 | #include 7 | #include 8 | 9 | template struct MaybeIfImpl; 10 | 11 | template 12 | class Maybe { 13 | public: 14 | Maybe(); 15 | Maybe(const Maybe& other); 16 | Maybe(Maybe&& other); 17 | Maybe(const T& other); 18 | Maybe(T&& other); 19 | ~Maybe() { clear(); } 20 | Maybe& operator=(const Maybe& other); 21 | Maybe& operator=(Maybe&& other); 22 | Maybe& operator=(const T& other); 23 | Maybe& operator=(T&& other); 24 | 25 | void clear(); 26 | 27 | bool is_set() const { return *is_set_ptr() != 0; } 28 | explicit operator bool() const { return is_set(); } 29 | 30 | // For use with decltype(...) 31 | T& infer_value_type() { ASSERT(false); return *((T*)nullptr); } 32 | const T& infer_value_type() const { ASSERT(false); return *((T*)nullptr); } 33 | 34 | template 35 | bool otherwise(Functor functor) { 36 | bool b = is_set(); 37 | if (!b) functor(); 38 | return b; 39 | } 40 | template 41 | bool otherwise(Functor functor) const { 42 | bool b = is_set(); 43 | if (!b) functor(); 44 | return b; 45 | } 46 | 47 | // Same as 'maybe_if', but with FP terminology a la Scala. 48 | template 49 | auto map(Functor functor) 50 | -> typename MaybeIfImpl, decltype(functor(infer_value_type()))>::ResultType { 51 | return MaybeIfImpl, decltype(functor(infer_value_type()))>::maybe_if(*this, functor); 52 | } 53 | template 54 | auto map(Functor functor) const 55 | -> typename MaybeIfImpl, decltype(functor(infer_value_type()))>::ResultType { 56 | return MaybeIfImpl, decltype(functor(infer_value_type()))>::maybe_if(*this, functor); 57 | } 58 | private: 59 | template friend struct MaybeIfImpl; 60 | 61 | const T* get() const { return is_set() ? memory() : nullptr; } 62 | T* get() { return is_set() ? memory() : nullptr; } 63 | T* operator->() { return get(); } 64 | const T* operator->() const { return get(); } 65 | T& operator*() { return *get(); } 66 | const T& operator*() const { return *get(); } 67 | 68 | static const size_t StorageSize = sizeof(T) + 1; // T + is_set byte 69 | static const size_t Alignment = std::alignment_of::value; 70 | typedef typename std::aligned_storage::type StorageType; 71 | 72 | StorageType memory_; 73 | 74 | const T* memory() const { return reinterpret_cast(&memory_); } 75 | T* memory() { return reinterpret_cast(&memory_); } 76 | const byte* is_set_ptr() const { return reinterpret_cast(&memory_) + sizeof(T); } 77 | byte* is_set_ptr() { return reinterpret_cast(&memory_) + sizeof(T); } 78 | 79 | void set(bool b) { 80 | if (b) *is_set_ptr() = 1; 81 | else *is_set_ptr() = 0; 82 | } 83 | 84 | template 85 | typename std::enable_if::Value && IsCopyAssignableNonRef::Value>::type 86 | assign(const T& by_copy) { 87 | if (is_set()) { 88 | *memory() = by_copy; 89 | } else { 90 | ::new(memory()) T(by_copy); 91 | set(true); 92 | } 93 | } 94 | 95 | template 96 | typename std::enable_if::Value && !IsCopyAssignableNonRef::Value>::type 97 | assign(const T& by_copy) { 98 | if (is_set()) { 99 | clear(); 100 | } 101 | ::new(memory()) T(by_copy); 102 | set(true); 103 | } 104 | 105 | template 106 | typename std::enable_if::Value && IsCopyAssignableNonRef::Value>::type 107 | assign(const T& by_copy) { 108 | if (!is_set()) { 109 | ::new(memory()) T; 110 | set(true); 111 | } 112 | *memory() = by_copy; 113 | } 114 | 115 | template 116 | typename std::enable_if::Value && IsMoveAssignableNonRef::Value>::type 117 | assign(T&& by_move) { 118 | if (is_set()) { 119 | *memory() = std::move(by_move); 120 | } else { 121 | ::new(memory()) T(std::move(by_move)); 122 | set(true); 123 | } 124 | } 125 | 126 | template 127 | typename std::enable_if::Value && !IsMoveAssignableNonRef::Value>::type 128 | assign(T&& by_move) { 129 | if (is_set()) { 130 | clear(); 131 | } 132 | ::new(memory()) T(std::move(by_move)); 133 | set(true); 134 | } 135 | 136 | template 137 | typename std::enable_if::Value && IsMoveAssignableNonRef::Value>::type 138 | assign(T&& by_move) { 139 | if (!is_set()) { 140 | ::new(memory()) T; 141 | set(true); 142 | } 143 | *memory() = std::move(by_move); 144 | } 145 | }; 146 | 147 | template 148 | Maybe::Maybe() { 149 | set(false); 150 | } 151 | 152 | template 153 | Maybe::Maybe(const Maybe& other) { 154 | set(false); 155 | if (other) assign(*other); 156 | } 157 | 158 | template 159 | Maybe::Maybe(Maybe&& other) { 160 | set(false); 161 | if (other) assign(std::move(*other)); 162 | other.clear(); 163 | } 164 | 165 | template 166 | Maybe::Maybe(const T& other) { 167 | set(false); 168 | assign(other); 169 | } 170 | 171 | template 172 | Maybe::Maybe(T&& other) { 173 | set(false); 174 | assign(std::move(other)); 175 | } 176 | 177 | template 178 | Maybe& Maybe::operator=(const Maybe& other) { 179 | if (other) assign(*other); 180 | else clear(); 181 | return *this; 182 | } 183 | 184 | template 185 | Maybe& Maybe::operator=(Maybe&& other) { 186 | if (other) { assign(std::move(*other)); other.clear(); } 187 | else clear(); 188 | return *this; 189 | } 190 | 191 | template 192 | Maybe& Maybe::operator=(const T& other) { 193 | assign(other); 194 | return *this; 195 | } 196 | 197 | template 198 | Maybe& Maybe::operator=(T&& other) { 199 | assign(std::move(other)); 200 | return *this; 201 | } 202 | 203 | template 204 | void Maybe::clear() { 205 | if (is_set()) { 206 | memory()->~T(); 207 | set(false); 208 | } 209 | } 210 | 211 | template 212 | struct RemoveMaybe; 213 | template 214 | struct RemoveMaybe> { 215 | typedef T Type; 216 | }; 217 | template 218 | struct RemoveMaybe { 219 | typedef T Type; 220 | }; 221 | 222 | struct BooleanHolder { 223 | BooleanHolder(bool value) : value_(value) {} 224 | bool value_; 225 | 226 | template 227 | bool otherwise(Functor functor) { 228 | if (!value_) functor(); 229 | return value_; 230 | } 231 | 232 | operator bool() const { return value_; } 233 | }; 234 | 235 | template 236 | struct MaybeIfImpl; 237 | 238 | template 239 | struct MaybeIfImpl { 240 | typedef BooleanHolder ResultType; 241 | 242 | template 243 | static BooleanHolder maybe_if(M& m, Functor function) { 244 | if (m) { function(*m); return true; } 245 | return false; 246 | } 247 | }; 248 | 249 | template 250 | struct MaybeIfImpl { 251 | typedef Maybe::Type> ResultType; 252 | 253 | template 254 | static ResultType maybe_if(M& m, Functor function) { 255 | if (m) return function(*m); 256 | return ResultType(); 257 | } 258 | }; 259 | 260 | 261 | template 262 | auto maybe_if(M& m, Functor function) 263 | -> typename MaybeIfImpl::ResultType 264 | { 265 | return MaybeIfImpl::maybe_if(m, function); 266 | } 267 | 268 | template 269 | auto maybe_if(M& m, Functor function) 270 | -> typename MaybeIfImpl::ResultType 271 | { 272 | return MaybeIfImpl::maybe_if(m, function); 273 | } 274 | 275 | template 276 | OutputStream& operator<<(OutputStream& os, const Maybe& m) { 277 | maybe_if(m, [&](const T& it) { os << it; }).otherwise([&]() { os << "(none)"; }); 278 | return os; 279 | } 280 | 281 | #endif /* end of include guard: MAYBE_HPP_8R2MUT0P */ 282 | -------------------------------------------------------------------------------- /base/maybe_type.cpp: -------------------------------------------------------------------------------- 1 | #include "base/maybe_type.hpp" 2 | 3 | std::string build_maybe_type_name(const Type* inner_type) { 4 | std::stringstream ss; 5 | ss << "Maybe<" << inner_type->name() << '>'; 6 | return ss.str(); 7 | } -------------------------------------------------------------------------------- /base/maybe_type.hpp: -------------------------------------------------------------------------------- 1 | #include "type/type.hpp" 2 | #include "base/maybe.hpp" 3 | #include "serialization/archive_node.hpp" 4 | 5 | std::string build_maybe_type_name(const Type* inner_type); 6 | 7 | template 8 | struct MaybeType : TypeFor> { 9 | MaybeType() : name_(build_maybe_type_name(get_type())) {} 10 | 11 | void deserialize(Maybe& place, const ArchiveNode&, IUniverse&) const; 12 | void serialize(const Maybe& place, ArchiveNode&, IUniverse&) const; 13 | 14 | const std::string& name() const { return name_; } 15 | 16 | const Type* inner_type() const { return get_type(); } 17 | private: 18 | std::string name_; 19 | }; 20 | 21 | template 22 | void MaybeType::deserialize(Maybe& m, const ArchiveNode& node, IUniverse& universe) const { 23 | if (!node.is_empty()) { 24 | T value; 25 | inner_type()->deserialize(reinterpret_cast(&value), node, universe); 26 | m = std::move(value); 27 | } 28 | } 29 | 30 | template 31 | void MaybeType::serialize(const Maybe& m, ArchiveNode& node, IUniverse& universe) const { 32 | m.map([&](const T& it) { 33 | inner_type()->serialize(reinterpret_cast(&it), node, universe); 34 | }); 35 | } 36 | 37 | template 38 | struct BuildTypeInfo> { 39 | static const MaybeType* build() { 40 | static const MaybeType type; 41 | return &type; 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /object/child_list.cpp: -------------------------------------------------------------------------------- 1 | #include "object/child_list.hpp" 2 | #include "serialization/deserialize_object.hpp" 3 | #include "serialization/serialize.hpp" 4 | 5 | void ChildListType::deserialize(ChildList& list, const ArchiveNode& node, IUniverse& universe) const { 6 | if (node.is_array()) { 7 | for (size_t i = 0; i < node.array_size(); ++i) { 8 | const ArchiveNode& child = node[i]; 9 | ObjectPtr<> ptr = deserialize_object(child, universe); 10 | if (ptr != nullptr) { 11 | list.push_back(std::move(ptr)); 12 | } 13 | } 14 | } 15 | } 16 | 17 | void ChildListType::serialize(const ChildList& list, ArchiveNode& node, IUniverse& universe) const { 18 | for (auto& child: list) { 19 | ArchiveNode& child_node = node.array_push(); 20 | ::serialize(*child, child_node, universe); 21 | } 22 | } -------------------------------------------------------------------------------- /object/child_list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef CHILD_LIST_HPP_B25F8VH4 3 | #define CHILD_LIST_HPP_B25F8VH4 4 | 5 | #include "base/array.hpp" 6 | #include "object/objectptr.hpp" 7 | #include "base/array_type.hpp" 8 | 9 | struct ChildList : Array> { 10 | ChildList() {} 11 | ChildList(const ChildList&) = delete; 12 | ChildList(ChildList&& other) = default; 13 | ChildList& operator=(ChildList&& other) = default; 14 | }; 15 | 16 | struct ChildListType : VariableLengthArrayType { 17 | ChildListType() : VariableLengthArrayType("ChildList") {} 18 | virtual ~ChildListType() {} 19 | void deserialize(ChildList& place, const ArchiveNode& node, IUniverse&) const; 20 | void serialize(const ChildList& place, ArchiveNode& node, IUniverse&) const override; 21 | }; 22 | 23 | template <> 24 | struct BuildTypeInfo { 25 | static const ChildListType* build() { 26 | static const ChildListType* type = new ChildListType; 27 | return type; 28 | } 29 | }; 30 | 31 | #endif /* end of include guard: CHILD_LIST_HPP_B25F8VH4 */ 32 | -------------------------------------------------------------------------------- /object/composite_type.cpp: -------------------------------------------------------------------------------- 1 | #include "object/composite_type.hpp" 2 | #include "object/struct_type.hpp" 3 | 4 | CompositeType::CompositeType(std::string name, const ObjectTypeBase* base_type) : base_type_(base_type), name_(std::move(name)), frozen_(false) { 5 | size_ = this->base_type()->size(); 6 | } 7 | 8 | void CompositeType::add_aspect(const DerivedType* aspect) { 9 | ASSERT(!frozen_); 10 | ASSERT(!aspect->is_abstract()); 11 | aspects_.push_back(aspect); // TODO: Check for circular dependencies. 12 | size_ += aspect->size(); 13 | } 14 | 15 | const ObjectTypeBase* CompositeType::base_type() const { 16 | return base_type_ ? base_type_ : get_type(); 17 | } 18 | 19 | Object* CompositeType::cast(const DerivedType* to, Object* o) const { 20 | return cast(to, o, nullptr); 21 | } 22 | 23 | Object* CompositeType::cast(const DerivedType* to, Object* o, const DerivedType* avoid) const { 24 | if (to == this) return o; 25 | 26 | // Check base type and aspects 27 | Object* result = find_instance_down(to, o, avoid); 28 | if (result != nullptr) return result; 29 | 30 | // Then check parent objects (that have this object as an aspect) 31 | return find_instance_up(to, o, this); 32 | } 33 | 34 | Object* CompositeType::find_instance_down(const DerivedType* to, Object* o, const DerivedType* avoid) const { 35 | // First check the base type 36 | Object* result = base_type()->cast(to, o); 37 | if (result != nullptr) return result; 38 | 39 | // Then do breadth-first search of aspects 40 | size_t offset = base_type()->size(); 41 | for (auto& aspect: aspects_) { 42 | Object* aspect_object = reinterpret_cast(reinterpret_cast(o) + offset); 43 | 44 | const CompositeType* composite_aspect = dynamic_cast(aspect); 45 | if (composite_aspect != nullptr) { 46 | if (composite_aspect != avoid) { 47 | Object* result = composite_aspect->find_instance_down(to, aspect_object, nullptr); 48 | if (result != nullptr) return result; 49 | } 50 | } else { 51 | Object* result = aspect->cast(to, aspect_object); 52 | if (result != nullptr) return result; 53 | } 54 | offset += aspect->size(); 55 | } 56 | 57 | return nullptr; // not found 58 | } 59 | 60 | Object* CompositeType::find_instance_up(const DerivedType* to, Object* object, const DerivedType* avoid) const { 61 | Object* o = object->find_parent(); 62 | if (o != nullptr) { 63 | const DerivedType* t = o->object_type(); 64 | const CompositeType* ct = dynamic_cast(t); 65 | if (ct != nullptr) { 66 | return ct->cast(to, o, this); 67 | } else { 68 | return t->cast(to, o); 69 | } 70 | } 71 | return nullptr; 72 | } 73 | 74 | Object* CompositeType::find_self_up(Object* object) const { 75 | Object* o = object; 76 | ssize_t offset = o->object_offset(); 77 | byte* memory = reinterpret_cast(o); 78 | while (true) { 79 | if (o->object_type() == this) return o; 80 | if (offset == 0) break; 81 | memory -= offset; 82 | o = reinterpret_cast(memory); 83 | offset = o->object_offset(); 84 | } 85 | return nullptr; 86 | } 87 | 88 | 89 | void CompositeType::construct(byte* place, IUniverse& universe) const { 90 | base_type()->construct(place, universe); 91 | Object* obj = reinterpret_cast(place); 92 | obj->set_object_type__(this); 93 | size_t offset = base_type()->size(); 94 | for (auto aspect: aspects_) { 95 | aspect->construct(place + offset, universe); 96 | Object* subobject = reinterpret_cast(place + offset); 97 | subobject->set_object_offset__(offset); 98 | subobject->set_object_id(aspect->name()); // might be renamed later by deserialization 99 | offset += aspect->size(); 100 | } 101 | ASSERT(offset == size_); 102 | } 103 | 104 | void CompositeType::destruct(byte* place, IUniverse& universe) const { 105 | Object* obj = reinterpret_cast(place); 106 | size_t offset = base_type()->size(); 107 | for (auto aspect: aspects_) { // TODO: Consider doing this backwards? 108 | aspect->destruct(place + offset, universe); 109 | offset += aspect->size(); 110 | } 111 | base_type()->destruct(place, universe); 112 | ASSERT(offset == size_); 113 | } 114 | 115 | void CompositeType::deserialize(byte* place, const ArchiveNode& node, IUniverse& universe) const { 116 | ASSERT(frozen_); 117 | base_type()->deserialize(place, node, universe); 118 | 119 | const ArchiveNode& aspect_array = node["aspects"]; 120 | if (aspect_array.is_array()) { 121 | size_t offset = base_type()->size(); 122 | size_t sz = aspect_array.array_size(); 123 | for (size_t i = 0; i < aspect_array.array_size(); ++i) { 124 | const ArchiveNode& aspect_node = aspect_array[i]; 125 | aspects_[i]->deserialize(place + offset, aspect_node, universe); 126 | Object* subobject = reinterpret_cast(place + offset); 127 | subobject->set_object_offset__(offset); 128 | offset += aspects_[i]->size(); 129 | } 130 | ASSERT(offset == size_); 131 | } 132 | } 133 | 134 | void CompositeType::serialize(const byte* place, ArchiveNode& node, IUniverse& universe) const { 135 | ASSERT(frozen_); 136 | base_type()->serialize(place, node, universe); 137 | node["class"] = base_type()->name(); 138 | 139 | size_t offset = base_type()->size(); 140 | ArchiveNode& aspect_array = node["aspects"]; 141 | for (auto aspect: aspects_) { 142 | ArchiveNode& aspect_node = aspect_array.array_push(); 143 | aspect->serialize(place + offset, aspect_node, universe); 144 | offset += aspect->size(); 145 | } 146 | ASSERT(offset == size_); 147 | } -------------------------------------------------------------------------------- /object/composite_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef COMPOSITE_TYPE_HPP_K5R3HGBW 3 | #define COMPOSITE_TYPE_HPP_K5R3HGBW 4 | 5 | #include "type/type.hpp" 6 | #include "serialization/archive.hpp" 7 | #include "base/array.hpp" 8 | #include 9 | 10 | struct CompositeType : DerivedType { 11 | CompositeType(std::string name, const ObjectTypeBase* base_type = nullptr); 12 | 13 | const ObjectTypeBase* base_type() const; 14 | void add_aspect(const DerivedType* aspect); 15 | void freeze() { frozen_ = true; } 16 | 17 | // Type interface 18 | void construct(byte* place, IUniverse&) const override; 19 | void destruct(byte* place, IUniverse&) const override; 20 | void deserialize(byte* place, const ArchiveNode& node, IUniverse&) const override; 21 | void serialize(const byte* place, ArchiveNode& node, IUniverse&) const override; 22 | const std::string& name() const override { return name_; } 23 | size_t size() const override { return size_; } 24 | 25 | // DerivedType interface 26 | size_t num_elements() const { return aspects_.size(); } 27 | size_t offset_of_element(size_t idx) const; 28 | const Type* type_of_element(size_t idx) const { return aspects_[idx]; } 29 | 30 | Object* cast(const DerivedType* to, Object* o) const override; 31 | Object* find_instance_down(const DerivedType* of_type, Object* o, const DerivedType* avoid = nullptr) const; 32 | Object* find_instance_up(const DerivedType* of_type, Object* o, const DerivedType* avoid = nullptr) const; 33 | Object* find_self_up(Object* o) const; 34 | private: 35 | Object* cast(const DerivedType* to, Object* o, const DerivedType* avoid) const; 36 | 37 | const ObjectTypeBase* base_type_; 38 | std::string name_; 39 | Array aspects_; 40 | bool frozen_; 41 | size_t size_; 42 | }; 43 | 44 | inline size_t CompositeType::offset_of_element(size_t idx) const { 45 | size_t offset = sizeof(Object); 46 | for (size_t i = 0; i < aspects_.size(); ++i) { 47 | if (i == idx) return offset; 48 | offset += aspects_[i]->size(); 49 | } 50 | ASSERT(false); // unreachable 51 | return SIZE_T_MAX; 52 | } 53 | 54 | #endif /* end of include guard: COMPOSITE_TYPE_HPP_K5R3HGBW */ 55 | -------------------------------------------------------------------------------- /object/object.cpp: -------------------------------------------------------------------------------- 1 | #include "object/object.hpp" 2 | #include "object/reflect.hpp" 3 | #include "object/universe.hpp" 4 | 5 | Object* Object::find_parent() { 6 | Object* object = this; 7 | if (object->offset_ != 0) { 8 | ssize_t offs = object->offset_; 9 | return reinterpret_cast(reinterpret_cast(object) - offs); 10 | } 11 | return nullptr; 12 | } 13 | 14 | const Object* Object::find_parent() const { 15 | const Object* object = this; 16 | if (object->offset_ != 0) { 17 | ssize_t offs = object->offset_; 18 | return reinterpret_cast(reinterpret_cast(object) - offs); 19 | } 20 | return nullptr; 21 | } 22 | 23 | Object* Object::find_topmost_object() { 24 | Object* object = this; 25 | Object* p; 26 | while ((p = object->find_parent()) != nullptr) { 27 | object = p; 28 | } 29 | return object; 30 | } 31 | 32 | const Object* Object::find_topmost_object() const { 33 | const Object* object = this; 34 | const Object* p; 35 | while ((p = object->find_parent()) != nullptr) { 36 | object = p; 37 | } 38 | return object; 39 | } 40 | 41 | bool Object::set_object_id(std::string new_id) { 42 | return universe_->rename_object(this, new_id); 43 | } 44 | 45 | const std::string& Object::object_id() const { 46 | return universe_->get_id(this); 47 | } 48 | 49 | BEGIN_TYPE_INFO(Object) 50 | abstract(); 51 | property(&Object::object_id, &Object::set_object_id, "id", "The unique ID for this object.h"); 52 | // property(&Object::id_, "ID", "The unique ID for this Object."); 53 | END_TYPE_INFO() -------------------------------------------------------------------------------- /object/object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef OBJECT_HPP_P40DARL9 3 | #define OBJECT_HPP_P40DARL9 4 | 5 | #include "base/basic.hpp" 6 | 7 | struct IUniverse; 8 | struct Type; 9 | struct DerivedType; 10 | struct ObjectTypeBase; 11 | template struct ObjectType; 12 | 13 | #define REFLECT \ 14 | public: \ 15 | static const bool has_reflection__ = true; \ 16 | typedef ObjectTypeBase TypeInfoType; \ 17 | static const TypeInfoType* build_type_info__(); 18 | 19 | template const Type* build_type_info(); // Only used for non-reflected types. 20 | 21 | struct Object { 22 | REFLECT; 23 | 24 | Object() : type_(nullptr), offset_(0), universe_(nullptr) {} 25 | virtual ~Object() {} 26 | 27 | Object* find_parent(); 28 | const Object* find_parent() const; 29 | Object* find_topmost_object(); 30 | const Object* find_topmost_object() const; 31 | 32 | IUniverse* universe() const { return universe_; } 33 | void set_universe__(IUniverse* universe) { universe_ = universe; } 34 | 35 | const std::string& object_id() const; 36 | bool set_object_id(std::string new_id); 37 | 38 | const DerivedType* object_type() const { return type_; } 39 | void set_object_type__(const DerivedType* t) { type_ = t; } 40 | size_t object_offset() const { return offset_; } 41 | void set_object_offset__(size_t o) { offset_ = o; } 42 | protected: 43 | void operator delete(void* ptr) {} 44 | void* operator new(size_t) { return nullptr; } 45 | private: 46 | const DerivedType* type_; 47 | size_t offset_; // offset within composite 48 | IUniverse* universe_; 49 | }; 50 | 51 | template 52 | struct IsDerivedFromObject { 53 | static const bool Value = std::is_convertible::type*, Object*>::value; 54 | }; 55 | 56 | struct CheckHasBuildTypeInfo { 57 | template 58 | struct Check {}; 59 | }; 60 | 61 | template 62 | struct HasReflection : HasMember {}; 63 | 64 | template 65 | typename std::enable_if::Value, const typename T::TypeInfoType*>::type 66 | get_type() { 67 | return T::build_type_info__(); 68 | } 69 | 70 | template 71 | typename std::enable_if::Value, const Type*>::type 72 | get_type() { 73 | return build_type_info(); 74 | } 75 | 76 | template 77 | typename std::enable_if::Value, const DerivedType*>::type 78 | get_type(const T& object) { 79 | return object.object_type(); 80 | } 81 | 82 | template 83 | typename std::enable_if::Value, const Type*>::type 84 | get_type(const T& value) { 85 | return get_type(); 86 | } 87 | 88 | inline const DerivedType* get_type(Object* object) { 89 | return object->object_type(); 90 | } 91 | 92 | inline const DerivedType* get_type(const Object* object) { 93 | return object->object_type(); 94 | } 95 | 96 | #endif /* end of include guard: OBJECT_HPP_P40DARL9 */ 97 | -------------------------------------------------------------------------------- /object/objectptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef OBJECTPTR_HPP_WICLN6JL 3 | #define OBJECTPTR_HPP_WICLN6JL 4 | 5 | #include "type/type.hpp" 6 | #include "object/object.hpp" 7 | #include "type/reference_type.hpp" 8 | 9 | template 10 | struct ObjectPtr; 11 | 12 | template 13 | struct ObjectPtr::Value>::type> { 14 | typedef T PointeeType; 15 | 16 | ObjectPtr() : ptr_(nullptr) {} 17 | ObjectPtr(T* ptr) : ptr_(ptr) {} 18 | template 19 | ObjectPtr(ObjectPtr other) { ptr_ = other.get(); } 20 | ObjectPtr(const ObjectPtr& other) { ptr_ = other.ptr_; } 21 | ObjectPtr(ObjectPtr&& other) { ptr_ = other.ptr_; } 22 | template 23 | ObjectPtr& operator=(U* other) { ptr_ = other; return *this; } 24 | template 25 | ObjectPtr& operator=(ObjectPtr other) { ptr_ = other.ptr_; return *this; } 26 | ObjectPtr& operator=(const ObjectPtr& other) { ptr_ = other.ptr_; return *this; } 27 | template 28 | bool operator==(ObjectPtr other) const { ptr_ == other.ptr_; } 29 | bool operator==(const ObjectPtr& other) const { return ptr_ == other.ptr_; } 30 | template 31 | bool operator!=(ObjectPtr other) const { ptr_ != other.ptr_; } 32 | bool operator!=(const ObjectPtr& other) const { return ptr_ != other.ptr_; } 33 | 34 | template 35 | ObjectPtr cast() const { 36 | return aspect_cast(ptr_); 37 | } 38 | 39 | T* get() const { return ptr_; } 40 | T* operator->() const { return ptr_; } 41 | T& operator*() const { return *ptr_; } 42 | const Type* type() const { return get_type(*ptr_); } 43 | 44 | 45 | bool operator<(const ObjectPtr& other) const { return ptr_ < other.ptr_; } 46 | private: 47 | T* ptr_; 48 | }; 49 | 50 | template 51 | struct BuildTypeInfo> { 52 | static const ReferenceTypeImpl>* build() { 53 | static const ReferenceTypeImpl> type("ObjectPtr"); 54 | return &type; 55 | } 56 | }; 57 | 58 | template 59 | ObjectPtr 60 | aspect_cast(ObjectPtr ptr) { 61 | return ObjectPtr(aspect_cast(ptr.get())); 62 | } 63 | 64 | template 65 | ObjectPtr<> 66 | aspect_cast(ObjectPtr ptr, const DerivedType* type) { 67 | return ObjectPtr<>(aspect_cast(ptr.get(), type)); 68 | } 69 | 70 | template 71 | OutputStream& operator<<(OutputStream& os, const ObjectPtr& ptr) { 72 | os << '(' << ptr.type()->name() << "*)" << ptr.get(); 73 | return os; 74 | } 75 | 76 | #endif /* end of include guard: OBJECTPTR_HPP_WICLN6JL */ 77 | -------------------------------------------------------------------------------- /object/reflect.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef REFLECT_HPP_WJBCX95G 3 | #define REFLECT_HPP_WJBCX95G 4 | 5 | #include "object/object.hpp" 6 | #include "object/struct_type.hpp" 7 | #include "type/attribute.hpp" 8 | #include "object/signal.hpp" 9 | #include 10 | 11 | template 12 | struct ObjectTypeBuilder { 13 | typedef ObjectTypeBuilder Self; 14 | 15 | ObjectTypeBuilder() : super_(nullptr), is_abstract_(false) {} 16 | 17 | Self& abstract(bool a = true) { is_abstract_ = true; return *this; } 18 | Self& name(std::string n) { name_ = std::move(n); return *this; } 19 | Self& description(std::string d) { description_ = std::move(d); return *this; } 20 | Self& super(const ObjectTypeBase* t) { super_ = t; return *this; } 21 | 22 | void check_attribute_name_(const std::string& name) { 23 | const auto reserved_names = {"class", "aspects"}; 24 | for (auto it: reserved_names) { 25 | if (name == it) { 26 | fprintf(stderr, "The attribute name '%s' is reserved.\n", name.c_str()); 27 | ASSERT(false); 28 | } 29 | } 30 | } 31 | 32 | template 33 | Self& property(MemberType T::* member, std::string name, std::string description/*, MemberType default_value = MemberType()*/) { 34 | check_attribute_name_(name); 35 | attributes_.push_back(new MemberAttribute(std::move(name), std::move(description), member)); 36 | return *this; 37 | } 38 | 39 | template 40 | Self& property(GetterReturnType (T::*getter)() const, SetterReturnType (T::*setter)(SetterArgumentType), std::string name, std::string description) { 41 | check_attribute_name_(name); 42 | typedef typename RemoveConstRef::Type RawType; 43 | attributes_.push_back(new MethodAttribute(std::move(name), std::move(description), getter, setter)); 44 | return *this; 45 | } 46 | 47 | template 48 | Self& signal(Signal T::* member, std::string name, std::string description) { 49 | return property(member, name, description); 50 | } 51 | 52 | template 53 | Self& slot(R(T::*function)(Args...), std::string name, std::string description) { 54 | slots_.push_back(new SlotAttribute(std::move(name), std::move(description), function)); 55 | return *this; 56 | } 57 | 58 | virtual void define__() = 0; 59 | 60 | ObjectType build__() { 61 | define__(); 62 | ObjectType type(super_, std::move(name_), std::move(description_)); 63 | type.set_abstract(is_abstract_); 64 | type.set_properties(std::move(attributes_)); 65 | type.set_slots(std::move(slots_)); 66 | return type; 67 | } 68 | 69 | const ObjectTypeBase* super_; 70 | bool is_abstract_; 71 | std::string name_; 72 | std::string description_; 73 | Array*> attributes_; 74 | Array*> slots_; 75 | }; 76 | 77 | #define BEGIN_TYPE_INFO(TYPE) \ 78 | const ObjectTypeBase* TYPE::build_type_info__() { \ 79 | static struct ObjectTypeBuilderImpl__ : ObjectTypeBuilder { \ 80 | void define__() override { name(#TYPE); 81 | 82 | #define END_TYPE_INFO() \ 83 | } \ 84 | } builder__; \ 85 | static const auto t = builder__.build__(); \ 86 | return &t; \ 87 | } 88 | 89 | #endif /* end of include guard: REFLECT_HPP_WJBCX95G */ 90 | -------------------------------------------------------------------------------- /object/signal.cpp: -------------------------------------------------------------------------------- 1 | #include "object/signal.hpp" 2 | #include "object/struct_type.hpp" 3 | #include 4 | 5 | std::string SignalTypeBase::build_signal_name(const Array& signature) { 6 | std::stringstream ss; 7 | ss << "Signal<"; 8 | for (size_t i = 0; i < signature.size(); ++i) { 9 | ss << signature[i]->name(); 10 | if (i+1 != signature.size()) { 11 | ss << ", "; 12 | } 13 | } 14 | ss << ">"; 15 | return ss.str(); 16 | } -------------------------------------------------------------------------------- /object/signal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef SIGNAL_HPP_IVWSWZJM 3 | #define SIGNAL_HPP_IVWSWZJM 4 | 5 | #include "base/basic.hpp" 6 | #include "type/type.hpp" 7 | #include "object/objectptr.hpp" 8 | #include "serialization/archive_node.hpp" 9 | 10 | #include 11 | #include 12 | #include // TODO: Get rid of. 13 | 14 | struct SlotAttributeBase; 15 | struct Object; 16 | struct ObjectTypeBase; 17 | 18 | struct SignalTypeBase; 19 | template struct SignalType; 20 | 21 | struct SlotInvokerBase { 22 | virtual ~SlotInvokerBase() {} 23 | virtual Object* receiver() const = 0; 24 | virtual const SlotAttributeBase* slot() const = 0; 25 | }; 26 | 27 | template 28 | struct SlotInvoker : SlotInvokerBase { 29 | virtual void invoke(Args...) const = 0; 30 | }; 31 | 32 | template 33 | class Signal { 34 | public: 35 | template 36 | void connect(Receiver* object, R(Receiver::*)(Args...)); 37 | template 38 | void connect(const Receiver* object, R(Receiver::*)(Args...) const); 39 | template 40 | void connect(ObjectPtr object, R(Receiver::*method)(Args...)) { connect(object.get(), method); } 41 | template 42 | void connect(ObjectPtr object, R(Receiver::*method)(Args...) const) { connect(object.get(), method); } 43 | template 44 | void connect(std::function); 45 | bool connect(ObjectPtr<> receiver, const SlotAttributeBase* slot); 46 | 47 | void invoke(Args...) const; 48 | void operator()(Args... args) const { invoke(std::forward(args)...); } 49 | 50 | size_t num_connections() const { return invokers_.size(); } 51 | const SlotInvoker* connection_at(size_t idx) const { return invokers_[idx]; } 52 | private: 53 | Array*> invokers_; 54 | }; 55 | 56 | struct SignalTypeBase : Type { 57 | public: 58 | virtual const Array& signature() const = 0; 59 | protected: 60 | static std::string build_signal_name(const Array& signature); 61 | }; 62 | 63 | template 64 | struct SignalType : TypeFor, SignalTypeBase> { 65 | void deserialize(Signal& place, const ArchiveNode&, IUniverse&) const; 66 | void serialize(const Signal& place, ArchiveNode&, IUniverse&) const; 67 | const std::string& name() const { return name_; } 68 | size_t size() const { return sizeof(Signal); } 69 | const Array& signature() const { return signature_; } 70 | 71 | SignalType() { 72 | build_signature(signature_); 73 | name_ = SignalTypeBase::build_signal_name(signature_); 74 | } 75 | private: 76 | std::string name_; 77 | Array signature_; 78 | }; 79 | 80 | template 81 | struct BuildTypeInfo> { 82 | static const SignalType* build() { 83 | static const SignalType type; 84 | return &type; 85 | } 86 | }; 87 | 88 | template 89 | struct MemberSlotInvoker : SlotInvoker { 90 | typedef R(T::*FunctionType)(Args...); 91 | T* object_; 92 | FunctionType member_; 93 | 94 | void invoke(Args... args) const { 95 | (object_->*member_)(std::forward(args)...); 96 | } 97 | 98 | Object* receiver() const { return object_; } 99 | 100 | const SlotAttributeBase* slot() const; 101 | 102 | MemberSlotInvoker(T* object, FunctionType member) : object_(object), member_(member) {} 103 | }; 104 | 105 | template 106 | struct FunctionInvoker : SlotInvoker { 107 | std::function function_; 108 | 109 | void invoke(Args... args) const { 110 | function_(std::forward(args)...); 111 | } 112 | 113 | FunctionInvoker(std::function function) : function_(std::move(function)) {} 114 | }; 115 | 116 | template 117 | void Signal::invoke(Args... args) const { 118 | for (auto& invoker: invokers_) { 119 | static_cast*>(invoker)->invoke(std::forward(args)...); 120 | } 121 | } 122 | 123 | template 124 | template 125 | void Signal::connect(Receiver* object, R(Receiver::*member)(Args...)) { 126 | invokers_.push_back(new MemberSlotInvoker(object, member)); 127 | } 128 | 129 | template 130 | template 131 | void Signal::connect(std::function function) { 132 | invokers_.push_back(new FunctionInvoker(function)); 133 | } 134 | 135 | struct SlotAttributeBase { 136 | SlotAttributeBase(std::string name, std::string description) : name_(name), description_(description) {} 137 | virtual ~SlotAttributeBase() {} 138 | const std::string& name() const { return name_; } 139 | const std::string& description() const { return description_; } 140 | virtual std::string signature_description() const = 0; 141 | const Array& signature() const { return signature_; } 142 | private: 143 | std::string name_; 144 | std::string description_; 145 | protected: 146 | Array signature_; 147 | }; 148 | 149 | template 150 | struct SlotForObject { 151 | virtual ~SlotForObject() {} 152 | virtual const std::string& slot_name() const = 0; 153 | }; 154 | 155 | template 156 | struct SlotWithSignature : SlotAttributeBase { 157 | SlotWithSignature(std::string name, std::string description) : SlotAttributeBase(name, description) { 158 | signature_.reserve(sizeof...(Args)); 159 | build_signature(signature_); 160 | } 161 | virtual ~SlotWithSignature() {} 162 | 163 | std::string signature_description() const override { 164 | return get_signature_description(); 165 | } 166 | 167 | virtual SlotInvoker* create_invoker(ObjectPtr<>) const = 0; 168 | }; 169 | 170 | template 171 | bool Signal::connect(ObjectPtr<> ptr, const SlotAttributeBase* slot) { 172 | auto slot_with_signature = dynamic_cast*>(slot); 173 | if (slot_with_signature == nullptr) { 174 | return false; 175 | } 176 | auto invoker = slot_with_signature->create_invoker(ptr); 177 | if (invoker == nullptr) { 178 | delete invoker; 179 | return false; 180 | } 181 | invokers_.push_back(invoker); 182 | return true; 183 | } 184 | 185 | template 186 | struct SlotAttribute : SlotForObject, SlotWithSignature { 187 | typedef R(T::*FunctionType)(Args...); 188 | 189 | SlotAttribute(std::string name, std::string description, FunctionType function) : SlotWithSignature(name, description), function_(function) {} 190 | 191 | FunctionType function_; 192 | 193 | FunctionType method() const { return function_; } 194 | 195 | SlotInvoker* create_invoker(ObjectPtr<> base_ptr) const { 196 | ObjectPtr ptr = aspect_cast(base_ptr); 197 | if (ptr == nullptr) return nullptr; 198 | return new MemberSlotInvoker(ptr.get(), function_); 199 | } 200 | 201 | const std::string& slot_name() const { return this->name(); } 202 | }; 203 | 204 | template 205 | void SignalType::deserialize(Signal& signal, const ArchiveNode& node, IUniverse&) const { 206 | if (node.is_array()) { 207 | for (size_t i = 0; i < node.array_size(); ++i) { 208 | const ArchiveNode& connection = node[i]; 209 | if (connection.is_map()) { 210 | const ArchiveNode& receiver_node = connection["receiver"]; 211 | const ArchiveNode& slot_node = connection["slot"]; 212 | std::string receiver; 213 | std::string slot; 214 | if (receiver_node.get(receiver) && slot_node.get(slot)) { 215 | node.register_signal_for_deserialization(&signal, receiver, slot); 216 | } else { 217 | std::cerr << "WARNING: Invalid signal connection."; 218 | } 219 | } else { 220 | std::cerr << "WARNING: Non-map signal connection node. Did you forget to write a scene upgrader?\n"; 221 | } 222 | } 223 | } 224 | } 225 | 226 | template 227 | void SignalType::serialize(const Signal& signal, ArchiveNode& node, IUniverse& universe) const { 228 | for (size_t i = 0; i < signal.num_connections(); ++i) { 229 | const SlotInvoker* invoker = signal.connection_at(i); 230 | ObjectPtr receiver = invoker->receiver(); 231 | const SlotAttributeBase* slot = invoker->slot(); 232 | if (receiver != nullptr && slot != nullptr) { 233 | ArchiveNode& signal_connection = node.array_push(); 234 | ArchiveNode& receiver_node = signal_connection["receiver"]; 235 | get_type>()->serialize(reinterpret_cast(&receiver), receiver_node, universe); // TODO! Prettier API…! 236 | signal_connection["slot"] = slot->name(); 237 | } 238 | } 239 | } 240 | 241 | #endif /* end of include guard: SIGNAL_HPP_IVWSWZJM */ 242 | -------------------------------------------------------------------------------- /object/struct_type.cpp: -------------------------------------------------------------------------------- 1 | #include "object/struct_type.hpp" 2 | #include "object/composite_type.hpp" 3 | 4 | const ObjectTypeBase* ObjectTypeBase::super() const { 5 | if (super_ != nullptr) return super_; 6 | const ObjectTypeBase* object_type = get_type(); 7 | return object_type != this ? object_type : nullptr; 8 | } 9 | 10 | Object* ObjectTypeBase::cast(const DerivedType* to, Object* o) const { 11 | const ObjectTypeBase* other = dynamic_cast(to); 12 | if (other != nullptr) { 13 | for (const ObjectTypeBase* t = other; t != nullptr; t = t->super_) { 14 | if (t == this) return o; // TODO: Consider what could be done for multiple inheritance? 15 | } 16 | return nullptr; 17 | } 18 | 19 | const CompositeType* comp = dynamic_cast(to); 20 | if (comp != nullptr) { 21 | return comp->find_self_up(o); 22 | } 23 | 24 | return nullptr; 25 | } -------------------------------------------------------------------------------- /object/struct_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef STRUCT_TYPE_HPP_PTB31EJN 3 | #define STRUCT_TYPE_HPP_PTB31EJN 4 | 5 | #include "type/type.hpp" 6 | #include 7 | 8 | #include 9 | #include "type/attribute.hpp" 10 | #include "object/signal.hpp" 11 | 12 | struct SlotAttributeBase; 13 | template struct SlotForObject; 14 | 15 | struct ObjectTypeBase : DerivedType { 16 | const std::string& name() const override { return name_; } 17 | const std::string& description() const { return description_; } 18 | Object* cast(const DerivedType* to, Object* o) const override; 19 | const ObjectTypeBase* super() const; 20 | virtual Array attributes() const = 0; 21 | virtual size_t num_slots() const = 0; 22 | virtual const SlotAttributeBase* slot_at(size_t idx) const = 0; 23 | 24 | template 25 | const SlotAttributeBase* find_slot_for_method(R(T::*method)(Args...)) const { 26 | size_t n = num_slots(); 27 | for (size_t i = 0; i < n; ++i) { 28 | const SlotAttributeBase* s = slot_at(i); 29 | const SlotAttribute* slot = dynamic_cast*>(s); 30 | if (slot != nullptr) { 31 | if (slot->method() == method) { 32 | return slot; 33 | } 34 | } 35 | } 36 | return nullptr; 37 | } 38 | protected: 39 | ObjectTypeBase(const ObjectTypeBase* super, std::string name, std::string description) : super_(super), name_(std::move(name)), description_(std::move(description)) {} 40 | 41 | const ObjectTypeBase* super_; 42 | std::string name_; 43 | std::string description_; 44 | }; 45 | 46 | template 47 | struct ObjectType : TypeFor { 48 | ObjectType(const ObjectTypeBase* super, std::string name, std::string description) : TypeFor(super, std::move(name), std::move(description)), is_abstract_(false) {} 49 | 50 | void construct(byte* place, IUniverse& universe) const { 51 | Object* p = ::new(place) T; 52 | p->set_object_type__(this); 53 | p->set_universe__(&universe); 54 | } 55 | 56 | void set_properties(Array*> properties) { 57 | properties_ = std::move(properties); 58 | } 59 | void set_slots(Array*> slots) { 60 | slots_ = std::move(slots); 61 | } 62 | 63 | Array attributes() const { 64 | Array result; 65 | result.resize(properties_.size()); 66 | for (auto& it: properties_) { 67 | result.push_back(dynamic_cast(it)); 68 | } 69 | return result; 70 | } 71 | size_t num_slots() const { return slots_.size(); } 72 | const SlotAttributeBase* slot_at(size_t idx) const { return dynamic_cast(slots_[idx]); } 73 | 74 | size_t num_elements() const { return properties_.size(); } 75 | const Type* type_of_element(size_t idx) const { return properties_[idx]->attribute_type(); } 76 | size_t offset_of_element(size_t idx) const { return 0; /* TODO */ } 77 | 78 | void deserialize(T& object, const ArchiveNode&, IUniverse&) const; 79 | void serialize(const T& object, ArchiveNode&, IUniverse&) const; 80 | 81 | void set_abstract(bool b) { is_abstract_ = b; } 82 | bool is_abstract() const { return is_abstract_; } 83 | 84 | const SlotAttributeBase* get_slot_by_name(const std::string& name) const { 85 | for (auto& it: slots_) { 86 | if (it->slot_name() == name) return dynamic_cast(it); 87 | } 88 | return nullptr; 89 | } 90 | protected: 91 | Array*> properties_; 92 | Array*> slots_; 93 | bool is_abstract_; 94 | }; 95 | 96 | 97 | template 98 | void ObjectType::deserialize(T& object, const ArchiveNode& node, IUniverse& universe) const { 99 | auto s = this->super(); 100 | if (s) s->deserialize(reinterpret_cast(&object), node, universe); 101 | 102 | for (auto& property: properties_) { 103 | property->deserialize_attribute(&object, node[property->attribute_name()], universe); 104 | } 105 | } 106 | 107 | template 108 | void ObjectType::serialize(const T& object, ArchiveNode& node, IUniverse& universe) const { 109 | auto s = this->super(); 110 | if (s) s->serialize(reinterpret_cast(&object), node, universe); 111 | 112 | for (auto& property: properties_) { 113 | property->serialize_attribute(&object, node[property->attribute_name()], universe); 114 | } 115 | node["class"] = this->name(); 116 | } 117 | 118 | template 119 | const SlotAttributeBase* MemberSlotInvoker::slot() const { 120 | const ObjectTypeBase* type = get_type(); 121 | return type->find_slot_for_method(member_); 122 | } 123 | 124 | #endif /* end of include guard: STRUCT_TYPE_HPP_PTB31EJN */ 125 | -------------------------------------------------------------------------------- /object/universe.cpp: -------------------------------------------------------------------------------- 1 | #include "object/universe.hpp" 2 | #include "object/struct_type.hpp" 3 | 4 | #include 5 | 6 | ObjectPtr<> TestUniverse::create_root(const DerivedType* type, std::string id) { 7 | clear(); 8 | root_ = create_object(type, std::move(id)); 9 | return root_; 10 | } 11 | 12 | ObjectPtr<> TestUniverse::create_object(const DerivedType* type, std::string id) { 13 | size_t sz = type->size(); 14 | byte* memory = new byte[sz]; 15 | type->construct(memory, *this); 16 | Object* object = reinterpret_cast(memory); 17 | memory_map_.push_back(object); 18 | rename_object(object, id); 19 | return object; 20 | } 21 | 22 | bool TestUniverse::rename_object(ObjectPtr<> object, std::string new_id) { 23 | ASSERT(object->universe() == this); 24 | 25 | // erase old name from database 26 | auto old_it = reverse_object_map_.find(object); 27 | if (old_it != reverse_object_map_.end()) { 28 | object_map_.erase(old_it->second); 29 | } 30 | 31 | // check if new name already exists 32 | auto it = object_map_.find(new_id); 33 | bool renamed_exact = true; 34 | std::string new_name; 35 | if ((it != object_map_.end()) || (new_id.size() < 2)) { 36 | // it does, so create a unique name from the requested name 37 | int n = 1; 38 | std::string base_name; 39 | if (new_id.size() >= 2) { 40 | std::stringstream recognize_number_at_end(new_id.substr(new_id.size()-2, 2)); 41 | if (!(recognize_number_at_end >> n).fail()) { 42 | base_name = new_id.substr(0, new_id.size()-2); 43 | } else { 44 | base_name = std::move(new_id); 45 | } 46 | n += 1; // n is set to 0 if recognition failed, otherwise the existing number. Add one. :) 47 | } else { 48 | base_name = object->object_type()->name(); 49 | } 50 | 51 | // increment n and try the name until we find one that's available 52 | do { 53 | std::stringstream create_new_name; 54 | create_new_name << base_name << std::setw(2) << std::setfill('0') << n; 55 | new_name = std::move(create_new_name.str()); 56 | } while (object_map_.find(new_name) != object_map_.end()); 57 | 58 | renamed_exact = false; 59 | } else { 60 | new_name = std::move(new_id); 61 | } 62 | 63 | object_map_[new_name] = object; 64 | reverse_object_map_[object] = std::move(new_name); 65 | return renamed_exact; 66 | } 67 | 68 | const std::string& TestUniverse::get_id(ObjectPtr object) const { 69 | auto it = reverse_object_map_.find(object); 70 | if (it != reverse_object_map_.end()) { 71 | return it->second; 72 | } 73 | return empty_id_; 74 | } 75 | 76 | void TestUniverse::clear() { 77 | for (auto object: memory_map_) { 78 | const DerivedType* type = object->object_type(); 79 | type->destruct(reinterpret_cast(object), *this); 80 | } 81 | // TODO: Test for references? 82 | object_map_.clear(); 83 | reverse_object_map_.clear(); 84 | memory_map_.clear(); 85 | } -------------------------------------------------------------------------------- /object/universe.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef UNIVERSE_HPP_VHU9428R 3 | #define UNIVERSE_HPP_VHU9428R 4 | 5 | #include 6 | #include 7 | 8 | 9 | #include "object/object.hpp" 10 | #include "object/objectptr.hpp" 11 | 12 | struct DerivedType; 13 | 14 | struct IUniverse { 15 | virtual ObjectPtr<> create_object(const DerivedType* type, std::string id) = 0; 16 | virtual ObjectPtr<> create_root(const DerivedType* type, std::string id) = 0; 17 | virtual ObjectPtr<> get_object(const std::string& id) const = 0; 18 | virtual const std::string& get_id(ObjectPtr object) const = 0; 19 | virtual bool rename_object(ObjectPtr<> object, std::string new_id) = 0; 20 | virtual ObjectPtr<> root() const = 0; 21 | virtual ~IUniverse() {} 22 | 23 | template 24 | ObjectPtr create(std::string id) { 25 | ObjectPtr<> o = this->create_object(get_type(), std::move(id)); 26 | ObjectPtr ptr = o.cast(); 27 | ASSERT(ptr != nullptr); // create_object did not create an instance of T. 28 | return ptr; 29 | } 30 | }; 31 | 32 | struct TestUniverse : IUniverse { 33 | ObjectPtr<> create_object(const DerivedType* type, std::string) override; 34 | ObjectPtr<> create_root(const DerivedType* type, std::string) override; 35 | ObjectPtr<> get_object(const std::string& id) const override { 36 | return find_or(object_map_, id, nullptr); 37 | } 38 | const std::string& get_id(ObjectPtr object) const override; 39 | bool rename_object(ObjectPtr<> object, std::string) override; 40 | ObjectPtr<> root() const override { return root_; } 41 | 42 | TestUniverse() : root_(nullptr) {} 43 | ~TestUniverse() { clear(); } 44 | private: 45 | void clear(); 46 | 47 | std::map> object_map_; 48 | std::map, std::string> reverse_object_map_; 49 | Array memory_map_; 50 | ObjectPtr<> root_; 51 | std::string empty_id_; 52 | }; 53 | 54 | #endif /* end of include guard: UNIVERSE_HPP_VHU9428R */ 55 | -------------------------------------------------------------------------------- /serialization/archive.cpp: -------------------------------------------------------------------------------- 1 | #include "serialization/archive.hpp" 2 | #include "object/objectptr.hpp" 3 | #include "serialization/serialize.hpp" 4 | #include 5 | #include 6 | #include 7 | #include "type/type_registry.hpp" 8 | #include "object/composite_type.hpp" 9 | #include "object/universe.hpp" 10 | #include "serialization/deserialize_object.hpp" 11 | 12 | void Archive::serialize(ObjectPtr<> object, IUniverse& universe) { 13 | ::serialize(*object, root(), universe); 14 | for (auto ref: serialize_references) { 15 | ref->perform(universe); 16 | } 17 | serialize_references.clear(); 18 | } 19 | 20 | ObjectPtr<> Archive::deserialize(IUniverse& universe) { 21 | const ArchiveNode& n = root(); 22 | ObjectPtr<> ptr = deserialize_object(root(), universe); 23 | 24 | for (auto it: deserialize_references) { 25 | it->perform(universe); 26 | } 27 | for (auto it: deserialize_signals) { 28 | it->perform(universe); 29 | } 30 | 31 | return ptr; 32 | 33 | if (!n.is_empty()) { 34 | std::string cls; 35 | if (!n["class"].get(cls)) { 36 | std::cerr << "ERROR: Object without class.\n"; 37 | return nullptr; 38 | } 39 | std::string id; 40 | if (!n["id"].get(id)) { 41 | std::cerr << "WARNING: Object without id.\n"; 42 | id = cls; 43 | } 44 | 45 | const ObjectTypeBase* struct_type = TypeRegistry::get(cls); 46 | if (struct_type == nullptr) { 47 | std::cerr << "ERROR: Type '" << cls << "' is not registered.\n"; 48 | return nullptr; 49 | } 50 | 51 | const DerivedType* type = struct_type; 52 | const ArchiveNode& aspects = n["aspects"]; 53 | if (aspects.is_array()) { 54 | CompositeType* t = new CompositeType(id + "Type", struct_type); 55 | 56 | for (size_t i = 0; i < aspects.array_size(); ++i) { 57 | const ArchiveNode& aspect = aspects[i]; 58 | std::string aspect_cls; 59 | if (!aspect["class"].get(aspect_cls)) { 60 | std::cerr << "ERROR: Aspect without class.\n"; 61 | return nullptr; 62 | } 63 | const ObjectTypeBase* aspect_type = TypeRegistry::get(aspect_cls); 64 | if (aspect_type != nullptr) { 65 | t->add_aspect(aspect_type); 66 | } else { 67 | std::cerr << "ERROR: Aspect type '" << aspect_cls << "' is not registered.\n"; 68 | return nullptr; 69 | } 70 | } 71 | 72 | t->freeze(); 73 | type = t; 74 | } 75 | 76 | ObjectPtr<> ptr = universe.create_object(type, id); 77 | if (ptr->object_id() != id) { 78 | std::cerr << "WARNING: Object '" << id << "' was renamed to '" << ptr->object_id() << "' because of a collision.\n"; 79 | } 80 | type->deserialize(reinterpret_cast(ptr.get()), n, universe); 81 | 82 | 83 | for (auto it: deserialize_references) { 84 | it->perform(universe); 85 | } 86 | for (auto it: deserialize_signals) { 87 | it->perform(universe); 88 | } 89 | 90 | return ptr; 91 | } 92 | return nullptr; 93 | } -------------------------------------------------------------------------------- /serialization/archive.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ARCHIVE_HPP_A0L9H8RE 3 | #define ARCHIVE_HPP_A0L9H8RE 4 | 5 | #include "base/basic.hpp" 6 | #include "object/object.hpp" 7 | #include "type/type.hpp" 8 | #include "object/objectptr.hpp" 9 | #include "serialization/archive_node_type.hpp" 10 | 11 | #include 12 | 13 | struct DeserializeReferenceBase; 14 | struct SerializeReferenceBase; 15 | struct DeserializeSignalBase; 16 | struct ArchiveNode; 17 | struct IUniverse; 18 | 19 | struct Archive { 20 | typedef ArchiveNodeType::Type NodeType; 21 | 22 | virtual ArchiveNode& root() = 0; 23 | virtual const ArchiveNode& root() const = 0; 24 | virtual void write(std::ostream& os) const = 0; 25 | virtual const ArchiveNode& operator[](const std::string& key) const = 0; 26 | virtual ArchiveNode& operator[](const std::string& key) = 0; 27 | virtual ArchiveNode* make(NodeType type = NodeType::Empty) = 0; 28 | virtual const ArchiveNode& empty() const = 0; 29 | 30 | void serialize(ObjectPtr<> object, IUniverse& universe); 31 | ObjectPtr<> deserialize(IUniverse& universe); 32 | 33 | void register_reference_for_deserialization(DeserializeReferenceBase* ref) { deserialize_references.push_back(ref); } 34 | void register_reference_for_serialization(SerializeReferenceBase* ref) { serialize_references.push_back(ref); } 35 | void register_signal_for_deserialization(DeserializeSignalBase* sig) { 36 | deserialize_signals.push_back(sig); 37 | } 38 | private: 39 | Array deserialize_references; 40 | Array serialize_references; 41 | Array deserialize_signals; 42 | }; 43 | 44 | #endif /* end of include guard: ARCHIVE_HPP_A0L9H8RE */ 45 | -------------------------------------------------------------------------------- /serialization/archive_node.cpp: -------------------------------------------------------------------------------- 1 | #include "serialization/archive_node.hpp" 2 | #include "serialization/archive.hpp" 3 | #include "object/universe.hpp" 4 | #include "object/objectptr.hpp" 5 | 6 | ArchiveNode& ArchiveNode::array_push() { 7 | if (type() != Type::Array) { 8 | clear(Type::Array); 9 | } 10 | ArchiveNode* n = archive_.make(); 11 | array_.push_back(n); 12 | return *n; 13 | } 14 | 15 | const ArchiveNode& ArchiveNode::operator[](size_t idx) const { 16 | ASSERT(type() == Type::Array); 17 | if (idx >= array_.size()) { 18 | return archive_.empty(); 19 | } 20 | return *array_[idx]; 21 | } 22 | 23 | ArchiveNode& ArchiveNode::operator[](size_t idx) { 24 | if (type() != Type::Array) { 25 | clear(Type::Array); 26 | } 27 | if (idx < array_.size()) { 28 | return *array_[idx]; 29 | } else { 30 | array_.reserve(idx+1); 31 | while (array_.size() < idx+1) { array_.push_back(archive_.make()); } 32 | return *array_[idx]; 33 | } 34 | } 35 | 36 | const ArchiveNode& ArchiveNode::operator[](const std::string& key) const { 37 | ASSERT(type() == Type::Map); 38 | return *find_or(map_, key, &archive_.empty()); 39 | } 40 | 41 | ArchiveNode& ArchiveNode::operator[](const std::string& key) { 42 | if (type() != Type::Map) { 43 | clear(Type::Map); 44 | } 45 | auto it = map_.find(key); 46 | if (it == map_.end()) { 47 | ArchiveNode* n = archive_.make(); 48 | map_[key] = n; 49 | return *n; 50 | } else { 51 | return *it->second; 52 | } 53 | } 54 | 55 | void ArchiveNode::register_reference_for_deserialization_impl(DeserializeReferenceBase* ref) const { 56 | archive_.register_reference_for_deserialization(ref); 57 | } 58 | 59 | void ArchiveNode::register_reference_for_serialization_impl(SerializeReferenceBase* ref) { 60 | archive_.register_reference_for_serialization(ref); 61 | } 62 | 63 | void ArchiveNode::register_signal_for_deserialization_impl(DeserializeSignalBase* sig) const { 64 | archive_.register_signal_for_deserialization(sig); 65 | } 66 | 67 | Object* DeserializeReferenceBase::get_object(IUniverse& universe) const { 68 | return universe.get_object(object_id_).get(); 69 | } 70 | 71 | std::string SerializeReferenceBase::get_id(const IUniverse& universe, Object* obj) const { 72 | return universe.get_id(obj); 73 | } 74 | 75 | Object* DeserializeSignalBase::get_object(const IUniverse& universe) const { 76 | return universe.get_object(receiver_id_).get(); 77 | } 78 | 79 | const SlotAttributeBase* DeserializeSignalBase::get_slot(Object* object) const { 80 | const DerivedType* type = get_type(object); 81 | return type->get_slot_by_name(slot_id_); 82 | } -------------------------------------------------------------------------------- /serialization/archive_node.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ARCHIVE_NODE_HPP_EP8GSONT 3 | #define ARCHIVE_NODE_HPP_EP8GSONT 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "serialization/archive_node_type.hpp" 10 | #include "type/type.hpp" 11 | 12 | struct Archive; 13 | struct DeserializeReferenceBase; 14 | struct SerializeReferenceBase; 15 | struct DeserializeSignalBase; 16 | struct IUniverse; 17 | struct SlotAttributeBase; 18 | struct DerivedType; 19 | 20 | struct ArchiveNode { 21 | typedef ArchiveNodeType::Type Type; 22 | 23 | bool is_empty() const { return type_ == Type::Empty; } 24 | bool is_array() const { return type_ == Type::Array; } 25 | bool is_map() const { return type_ == Type::Map; } 26 | Type type() const { return type_; } 27 | 28 | bool get(float32&) const; 29 | bool get(float64&) const; 30 | bool get(int8&) const; 31 | bool get(int16&) const; 32 | bool get(int32&) const; 33 | bool get(int64&) const; 34 | bool get(uint8&) const; 35 | bool get(uint16&) const; 36 | bool get(uint32&) const; 37 | bool get(uint64&) const; 38 | bool get(std::string&) const; 39 | void set(float32); 40 | void set(float64); 41 | void set(int8); 42 | void set(int16); 43 | void set(int32); 44 | void set(int64); 45 | void set(uint8); 46 | void set(uint16); 47 | void set(uint32); 48 | void set(uint64); 49 | void set(std::string); 50 | void clear() { clear(Type::Empty); } 51 | 52 | const ArchiveNode& operator[](size_t idx) const; 53 | ArchiveNode& operator[](size_t idx); 54 | const ArchiveNode& operator[](const std::string& key) const; 55 | ArchiveNode& operator[](const std::string& key); 56 | 57 | ArchiveNode& array_push(); 58 | size_t array_size() const { return array_.size(); } 59 | 60 | template 61 | ArchiveNode& operator=(T value) { 62 | this->set(value); 63 | return *this; 64 | } 65 | 66 | virtual ~ArchiveNode() {} 67 | virtual void write(std::ostream& os) const = 0; 68 | 69 | template 70 | void register_reference_for_deserialization(T& reference) const; 71 | template 72 | void register_reference_for_serialization(const T& reference); 73 | template 74 | void register_signal_for_deserialization(T* signal, std::string receiver_id, std::string slot_id) const; 75 | protected: 76 | explicit ArchiveNode(Archive& archive, Type t = Type::Empty) : archive_(archive), type_(t) {} 77 | protected: 78 | Archive& archive_; 79 | Type type_; 80 | // TODO: Use an 'any'/'variant' type for the following: 81 | std::map map_; 82 | Array array_; 83 | std::string string_value; 84 | union { 85 | int64 integer_value; 86 | float64 float_value; 87 | }; 88 | 89 | void clear(ArchiveNodeType::Type new_node_type); 90 | template 91 | bool get_value(T& v, Type value_type, const U& value) const; 92 | 93 | void register_reference_for_deserialization_impl(DeserializeReferenceBase* ref) const; 94 | void register_reference_for_serialization_impl(SerializeReferenceBase* ref); 95 | void register_signal_for_deserialization_impl(DeserializeSignalBase* sig) const; 96 | }; 97 | 98 | inline void ArchiveNode::set(float32 f) { 99 | clear(Type::Float); 100 | float_value = f; 101 | } 102 | 103 | inline void ArchiveNode::set(float64 f) { 104 | clear(Type::Float); 105 | float_value = f; 106 | } 107 | 108 | inline void ArchiveNode::set(int8 n) { 109 | clear(Type::Integer); 110 | integer_value = n; 111 | } 112 | inline void ArchiveNode::set(int16 n) { 113 | clear(Type::Integer); 114 | integer_value = n; 115 | } 116 | 117 | inline void ArchiveNode::set(int32 n) { 118 | clear(Type::Integer); 119 | integer_value = n; 120 | } 121 | 122 | inline void ArchiveNode::set(int64 n) { 123 | clear(Type::Integer); 124 | integer_value = n; 125 | } 126 | 127 | inline void ArchiveNode::set(uint8 n) { 128 | clear(Type::Integer); 129 | integer_value = n; 130 | } 131 | 132 | inline void ArchiveNode::set(uint16 n) { 133 | clear(Type::Integer); 134 | integer_value = n; 135 | } 136 | 137 | inline void ArchiveNode::set(uint32 n) { 138 | clear(Type::Integer); 139 | integer_value = n; 140 | } 141 | 142 | inline void ArchiveNode::set(uint64 n) { 143 | clear(Type::Integer); 144 | integer_value = n; 145 | } 146 | 147 | inline void ArchiveNode::set(std::string s) { 148 | clear(Type::String); 149 | string_value = std::move(s); 150 | } 151 | 152 | template 153 | bool ArchiveNode::get_value(T& out_value, ArchiveNodeType::Type value_type, const U& value) const { 154 | if (type() == value_type) { 155 | out_value = value; 156 | return true; 157 | } 158 | return false; 159 | } 160 | 161 | inline bool ArchiveNode::get(float32& v) const { 162 | return get_value(v, Type::Float, float_value); 163 | } 164 | 165 | inline bool ArchiveNode::get(float64& v) const { 166 | return get_value(v, Type::Float, float_value); 167 | } 168 | 169 | inline bool ArchiveNode::get(int8& v) const { 170 | return get_value(v, Type::Integer, integer_value); 171 | } 172 | 173 | inline bool ArchiveNode::get(int16& v) const { 174 | return get_value(v, Type::Integer, integer_value); 175 | } 176 | 177 | inline bool ArchiveNode::get(int32& v) const { 178 | return get_value(v, Type::Integer, integer_value); 179 | } 180 | 181 | inline bool ArchiveNode::get(int64& v) const { 182 | return get_value(v, Type::Integer, integer_value); 183 | } 184 | 185 | inline bool ArchiveNode::get(uint8& v) const { 186 | return get_value(v, Type::Integer, integer_value); 187 | } 188 | 189 | inline bool ArchiveNode::get(uint16& v) const { 190 | return get_value(v, Type::Integer, integer_value); 191 | } 192 | 193 | inline bool ArchiveNode::get(uint32& v) const { 194 | return get_value(v, Type::Integer, integer_value); 195 | } 196 | 197 | inline bool ArchiveNode::get(uint64& v) const { 198 | return get_value(v, Type::Integer, integer_value); 199 | } 200 | 201 | inline bool ArchiveNode::get(std::string& s) const { 202 | return get_value(s, Type::String, string_value); 203 | } 204 | 205 | inline void ArchiveNode::clear(ArchiveNodeType::Type new_type) { 206 | map_.clear(); 207 | array_.clear(); 208 | string_value = ""; 209 | integer_value = 0; 210 | type_ = new_type; 211 | } 212 | 213 | struct DeserializeReferenceBase { 214 | virtual ~DeserializeReferenceBase() {} 215 | DeserializeReferenceBase(std::string object_id) : object_id_(object_id) {} 216 | virtual void perform(IUniverse&) = 0; 217 | protected: 218 | std::string object_id_; 219 | Object* get_object(IUniverse&) const; 220 | }; 221 | 222 | template 223 | struct DeserializeReference : DeserializeReferenceBase { 224 | public: 225 | typedef typename T::PointeeType PointeeType; 226 | 227 | DeserializeReference(std::string object_id, T& reference) : DeserializeReferenceBase(object_id), reference_(reference) {} 228 | void perform(IUniverse& universe) { 229 | Object* object_ptr = get_object(universe); 230 | if (object_ptr == nullptr) { 231 | // TODO: Warn about non-existing object ID. 232 | } 233 | PointeeType* ptr = aspect_cast(object_ptr); 234 | if (ptr == nullptr) { 235 | // TODO: Warn about type mismatch. 236 | } 237 | reference_ = ptr; 238 | } 239 | private: 240 | T& reference_; 241 | }; 242 | 243 | template 244 | void ArchiveNode::register_reference_for_deserialization(T& reference) const { 245 | std::string id; 246 | if (get(id)) { 247 | register_reference_for_deserialization_impl(new DeserializeReference(id, reference)); 248 | } 249 | } 250 | 251 | struct SerializeReferenceBase { 252 | virtual ~SerializeReferenceBase() {} 253 | SerializeReferenceBase(ArchiveNode& node) : node_(node) {} 254 | virtual void perform(const IUniverse&) = 0; 255 | protected: 256 | ArchiveNode& node_; 257 | std::string get_id(const IUniverse&, Object*) const; 258 | }; 259 | 260 | template 261 | struct SerializeReference : SerializeReferenceBase { 262 | typedef typename T::PointeeType PointeeType; 263 | 264 | SerializeReference(ArchiveNode& node, const T& reference) : SerializeReferenceBase(node), reference_(reference) {} 265 | void perform(const IUniverse& universe) { 266 | if (reference_ != nullptr) { 267 | node_.set(get_id(universe, reference_.get())); 268 | } else { 269 | node_.clear(); 270 | } 271 | } 272 | private: 273 | T reference_; 274 | }; 275 | 276 | template 277 | void ArchiveNode::register_reference_for_serialization(const T& reference) { 278 | register_reference_for_serialization_impl(new SerializeReference(*this, reference)); 279 | } 280 | 281 | struct DeserializeSignalBase { 282 | public: 283 | virtual void perform(const IUniverse&) const = 0; 284 | protected: 285 | DeserializeSignalBase(std::string receiver, std::string slot) : receiver_id_(std::move(receiver)), slot_id_(std::move(slot)) {} 286 | std::string receiver_id_; 287 | std::string slot_id_; 288 | 289 | Object* get_object(const IUniverse&) const; 290 | const SlotAttributeBase* get_slot(Object*) const; 291 | }; 292 | 293 | template 294 | struct DeserializeSignal : DeserializeSignalBase { 295 | DeserializeSignal(T* signal, std::string receiver, std::string slot) : DeserializeSignalBase(std::move(receiver), std::move(slot)), signal_(signal) {} 296 | 297 | void perform(const IUniverse& universe) const { 298 | Object* object = get_object(universe); 299 | if (object == nullptr) return; 300 | const SlotAttributeBase* slot = get_slot(object); 301 | if (slot == nullptr) return; 302 | signal_->connect(object, slot); 303 | } 304 | private: 305 | T* signal_; 306 | }; 307 | 308 | template 309 | void ArchiveNode::register_signal_for_deserialization(T* signal, std::string receiver, std::string slot) const { 310 | register_signal_for_deserialization_impl(new DeserializeSignal(signal, std::move(receiver), std::move(slot))); 311 | } 312 | 313 | #endif /* end of include guard: ARCHIVE_NODE_HPP_EP8GSONT */ 314 | -------------------------------------------------------------------------------- /serialization/archive_node_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ARCHIVE_NODE_TYPE_HPP_LDO8RRDW 3 | #define ARCHIVE_NODE_TYPE_HPP_LDO8RRDW 4 | 5 | struct ArchiveNodeType { 6 | enum Type { 7 | Empty, 8 | Array, 9 | Map, 10 | Integer, 11 | Float, 12 | String, 13 | }; 14 | }; 15 | 16 | #endif /* end of include guard: ARCHIVE_NODE_TYPE_HPP_LDO8RRDW */ 17 | -------------------------------------------------------------------------------- /serialization/deserialize_object.cpp: -------------------------------------------------------------------------------- 1 | #include "serialization/deserialize_object.hpp" 2 | #include "serialization/archive_node.hpp" 3 | #include "type/type_registry.hpp" 4 | #include "object/composite_type.hpp" 5 | #include "object/struct_type.hpp" 6 | #include "object/universe.hpp" 7 | #include 8 | 9 | namespace { 10 | const DerivedType* get_type_from_map(const ArchiveNode& node, std::string& out_error); 11 | 12 | const ObjectTypeBase* get_class_from_map(const ArchiveNode& node, std::string& out_error) { 13 | std::string clsname; 14 | if (!node["class"].get(clsname)) { 15 | out_error = "Class not specified."; 16 | return nullptr; 17 | } 18 | const ObjectTypeBase* struct_type = TypeRegistry::get(clsname); 19 | if (struct_type == nullptr) { 20 | out_error = "Class '" + clsname + "' not registered."; 21 | return nullptr; 22 | } 23 | return struct_type; 24 | } 25 | 26 | const DerivedType* transform_if_composite_type(const ArchiveNode& node, const ObjectTypeBase* base_type, std::string& out_error) { 27 | const ArchiveNode& aspects = node["aspects"]; 28 | if (!aspects.is_array()) return base_type; 29 | if (aspects.array_size() == 0) return base_type; 30 | 31 | CompositeType* type = new CompositeType("Composite", base_type); 32 | for (size_t i = 0; i < aspects.array_size(); ++i) { 33 | const ArchiveNode& aspect = aspects[i]; 34 | const DerivedType* aspect_type = get_type_from_map(aspect, out_error); 35 | if (aspect_type == nullptr) { 36 | return nullptr; 37 | } 38 | type->add_aspect(aspect_type); 39 | } 40 | type->freeze(); 41 | return type; 42 | } 43 | 44 | const DerivedType* get_type_from_map(const ArchiveNode& node, std::string& out_error) { 45 | const ObjectTypeBase* struct_type = get_class_from_map(node, out_error); 46 | if (struct_type != nullptr) { 47 | return transform_if_composite_type(node, struct_type, out_error); 48 | } 49 | return nullptr; 50 | } 51 | } 52 | 53 | ObjectPtr<> deserialize_object(const ArchiveNode& node, IUniverse& universe) { 54 | if (!node.is_map()) { 55 | std::cerr << "Expected object, got non-map.\n"; 56 | return nullptr; 57 | } 58 | 59 | std::string error; 60 | const DerivedType* type = get_type_from_map(node, error); 61 | if (type == nullptr) { 62 | std::cerr << "ERROR: " << error << '\n'; 63 | return nullptr; 64 | } 65 | 66 | std::string id; 67 | if (!node["id"].get(id)) { 68 | std::cerr << "WARNING: Object without id.\n"; 69 | } 70 | 71 | ObjectPtr<> ptr = universe.create_object(type, id); 72 | if (ptr->object_id() != id) { 73 | std::cerr << "WARNING: Object '" << id << "' was renamed to '" << ptr->object_id() << "' because of a collision.\n"; 74 | } 75 | 76 | type->deserialize(reinterpret_cast(ptr.get()), node, universe); 77 | 78 | return ptr; 79 | } 80 | -------------------------------------------------------------------------------- /serialization/deserialize_object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef DESERIALIZE_OBJECT_HPP_F2934JFR 3 | #define DESERIALIZE_OBJECT_HPP_F2934JFR 4 | 5 | #include "object/objectptr.hpp" 6 | 7 | struct IUniverse; 8 | struct ArchiveNode; 9 | 10 | ObjectPtr<> deserialize_object(const ArchiveNode& representation, IUniverse& universe); 11 | 12 | #endif /* end of include guard: DESERIALIZE_OBJECT_HPP_F2934JFR */ 13 | -------------------------------------------------------------------------------- /serialization/json_archive.cpp: -------------------------------------------------------------------------------- 1 | #include "serialization/json_archive.hpp" 2 | 3 | JSONArchive::JSONArchive() : root_(nullptr) { 4 | empty_ = make_internal(); 5 | } 6 | 7 | JSONArchiveNode* JSONArchive::make_internal(ArchiveNode::Type node_type) { 8 | return nodes_.allocate(*this, node_type); 9 | } 10 | 11 | ArchiveNode& JSONArchive::root() { 12 | if (root_ == nullptr) { 13 | root_ = make_internal(ArchiveNodeType::Map); 14 | } 15 | return *root_; 16 | } 17 | 18 | const ArchiveNode& JSONArchive::root() const { 19 | ASSERT(root_ != nullptr); 20 | return *root_; 21 | } 22 | 23 | void JSONArchive::write(std::ostream& os) const { 24 | os << "{ \"root\": "; 25 | if (root_ != nullptr) 26 | root_->write(os, false, 1); 27 | os << "\n}\n"; 28 | } 29 | 30 | const ArchiveNode& JSONArchive::operator[](const std::string& key) const { 31 | return root()[key]; 32 | } 33 | 34 | ArchiveNode& JSONArchive::operator[](const std::string& key) { 35 | return root()[key]; 36 | } 37 | 38 | static void print_indentation(std::ostream& os, int level) { 39 | for (int i = 0; i < level; ++i) { 40 | os << " "; 41 | } 42 | } 43 | 44 | static void print_string(std::ostream& os, const std::string& str) { 45 | // TODO: Escape 46 | os << '"'; 47 | os << str; 48 | os << '"'; 49 | } 50 | 51 | void JSONArchiveNode::write(std::ostream& os, bool print_inline, int indent) const { 52 | switch (type()) { 53 | case ArchiveNodeType::Empty: os << "null"; break; 54 | case ArchiveNodeType::Array: { 55 | os << '['; 56 | if (print_inline) { 57 | for (size_t i = 0; i < array_.size(); ++i) { 58 | dynamic_cast(array_[i])->write(os, true, indent); 59 | if (i+1 != array_.size()) { 60 | os << ", "; 61 | } 62 | } 63 | } else { 64 | for (size_t i = 0; i < array_.size(); ++i) { 65 | os << '\n'; 66 | print_indentation(os, indent+1); 67 | dynamic_cast(array_[i])->write(os, indent > 2, indent+1); 68 | if (i+1 != array_.size()) { 69 | os << ','; 70 | } 71 | } 72 | os << '\n'; 73 | print_indentation(os, indent); 74 | } 75 | os << ']'; 76 | break; 77 | } 78 | case ArchiveNodeType::Map: { 79 | os << '{'; 80 | if (print_inline) { 81 | for (auto it = map_.begin(); it != map_.end();) { 82 | print_string(os, it->first); 83 | os << ": "; 84 | dynamic_cast(it->second)->write(os, true, indent); 85 | ++it; 86 | if (it != map_.end()) { 87 | os << ", "; 88 | } 89 | } 90 | } else { 91 | for (auto it = map_.begin(); it != map_.end();) { 92 | os << '\n'; 93 | print_indentation(os, indent+1); 94 | print_string(os, it->first); 95 | os << ": "; 96 | dynamic_cast(it->second)->write(os, indent > 2, indent+1); 97 | ++it; 98 | if (it != map_.end()) { 99 | os << ','; 100 | } 101 | } 102 | os << '\n'; 103 | print_indentation(os, indent); 104 | } 105 | os << '}'; 106 | break; 107 | } 108 | case ArchiveNodeType::Integer: os << integer_value; break; 109 | case ArchiveNodeType::Float: os << float_value; break; 110 | case ArchiveNodeType::String: print_string(os, string_value); break; 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /serialization/json_archive.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef JSON_ARCHIVE_HPP_4OX35IUJ 3 | #define JSON_ARCHIVE_HPP_4OX35IUJ 4 | 5 | #include "serialization/archive.hpp" 6 | #include "serialization/archive_node.hpp" 7 | #include "base/bag.hpp" 8 | #include 9 | #include 10 | 11 | 12 | struct JSONArchive; 13 | 14 | struct JSONArchiveNode : ArchiveNode { 15 | JSONArchiveNode(JSONArchive& archive, ArchiveNodeType::Type t = ArchiveNodeType::Empty); 16 | void write(std::ostream& os) const override { write(os, false, 0); } 17 | void write(std::ostream& os, bool print_inline, int indent) const; 18 | }; 19 | 20 | struct JSONArchive : Archive { 21 | JSONArchive(); 22 | ArchiveNode& root() override; 23 | const ArchiveNode& root() const override; 24 | void write(std::ostream& os) const override; 25 | const ArchiveNode& operator[](const std::string& key) const override; 26 | ArchiveNode& operator[](const std::string& key) override; 27 | ArchiveNode* make(ArchiveNode::Type t = ArchiveNodeType::Empty) override { return make_internal(t); } 28 | 29 | const ArchiveNode& empty() const { return *empty_; } 30 | private: 31 | friend struct JSONArchiveNode; 32 | JSONArchiveNode* empty_; 33 | JSONArchiveNode* root_; 34 | ContainedBag nodes_; 35 | JSONArchiveNode* make_internal(ArchiveNodeType::Type t = ArchiveNodeType::Empty); 36 | }; 37 | 38 | inline JSONArchiveNode::JSONArchiveNode(JSONArchive& archive, ArchiveNode::Type t) : ArchiveNode(archive, t) {} 39 | 40 | #endif /* end of include guard: JSON_ARCHIVE_HPP_4OX35IUJ */ 41 | -------------------------------------------------------------------------------- /serialization/serialize.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef SERIALIZE_HPP_37QGG4TA 3 | #define SERIALIZE_HPP_37QGG4TA 4 | 5 | #include "serialization/archive.hpp" 6 | #include "object/struct_type.hpp" 7 | 8 | template 9 | void serialize(const T& object, ArchiveNode& node, IUniverse& universe) { 10 | const byte* memory = reinterpret_cast(&object); 11 | get_type(object)->serialize(memory, node, universe); 12 | } 13 | 14 | #endif /* end of include guard: SERIALIZE_HPP_37QGG4TA */ 15 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "object/object.hpp" 7 | #include "object/objectptr.hpp" 8 | #include "object/reflect.hpp" 9 | #include "type/type.hpp" 10 | #include "object/composite_type.hpp" 11 | #include "object/struct_type.hpp" 12 | #include "base/array_type.hpp" 13 | #include "serialization/json_archive.hpp" 14 | #include "object/universe.hpp" 15 | #include "base/maybe.hpp" 16 | #include "base/maybe_type.hpp" 17 | #include "type/type_registry.hpp" 18 | #include "object/child_list.hpp" 19 | 20 | struct Scene : Object { 21 | REFLECT; 22 | 23 | ChildList children; 24 | }; 25 | 26 | BEGIN_TYPE_INFO(Scene) 27 | property(&Scene::children, "Children", "The objects of the scene."); 28 | END_TYPE_INFO() 29 | 30 | struct Foo : Object { 31 | REFLECT; 32 | 33 | Maybe foo; 34 | void a_signal_receiver(int n) { std::cout << "Foo::a_signal_receiver: " << n << "\n"; an_empty_signal(); } 35 | Signal after_signal_received; 36 | Signal<> an_empty_signal; 37 | 38 | Foo() : foo(123) {} 39 | }; 40 | 41 | BEGIN_TYPE_INFO(Foo) 42 | description("Foo is a class."); 43 | property(&Foo::foo, "foo", "A number."); 44 | slot(&Foo::a_signal_receiver, "Signal receiver", "It's a slot."); 45 | signal(&Foo::after_signal_received, "After signal received", "It's a signal."); 46 | signal(&Foo::an_empty_signal, "Empty signal", "blah blah"); 47 | END_TYPE_INFO() 48 | 49 | struct Bar : Object { 50 | REFLECT; 51 | 52 | int bar; 53 | Array list; 54 | ObjectPtr foo; 55 | Bar() : bar(456), foo(nullptr) {} 56 | ~Bar() { 57 | std::cout << "~Bar " << this << '\n'; 58 | } 59 | 60 | Signal when_something_happens; 61 | }; 62 | 63 | BEGIN_TYPE_INFO(Bar) 64 | description("Bar is a class."); 65 | property(&Bar::bar, "bar", "Another number"); 66 | property(&Bar::list, "list", "A list of numbers"); 67 | property(&Bar::foo, "foo", "A reference to a foo"); 68 | signal(&Bar::when_something_happens, "when_something_happens", "La la la"); 69 | END_TYPE_INFO() 70 | 71 | int main (int argc, char const *argv[]) 72 | { 73 | TypeRegistry::add(); 74 | TypeRegistry::add(); 75 | TypeRegistry::add(); 76 | TypeRegistry::add(); 77 | 78 | TestUniverse universe; 79 | 80 | auto t = new CompositeType("FooBar", get_type()); 81 | t->add_aspect(get_type()); 82 | t->add_aspect(get_type()); 83 | t->freeze(); 84 | 85 | ObjectPtr<> p = universe.create_object(t, "Composite FooBar"); 86 | ObjectPtr b = universe.create("Bar"); 87 | ObjectPtr scene = p.cast(); 88 | scene->children.push_back(b); 89 | 90 | ObjectPtr bar = aspect_cast(p); 91 | ObjectPtr foo = aspect_cast(p); 92 | bar->foo = foo; 93 | bar->when_something_happens.connect(foo, &Foo::a_signal_receiver); 94 | bar->when_something_happens(bar->bar); 95 | 96 | JSONArchive json; 97 | json.serialize(p, universe); 98 | json.write(std::cout); 99 | 100 | TestUniverse universe2; 101 | ObjectPtr<> root = json.deserialize(universe2); 102 | ObjectPtr bar2 = aspect_cast(root); 103 | bar2->when_something_happens(bar2->bar); 104 | 105 | JSONArchive json2; 106 | json2.serialize(root, universe2); 107 | json2.write(std::cout); 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS = -O0 -g -I.. -std=c++11 -stdlib=libc++ -Wall -Werror -Wno-unused 2 | CXX = clang++ 3 | COMPILE = $(CXX) $(CXXFLAGS) 4 | 5 | maybe_test: maybe_test.cpp ../maybe.hpp 6 | $(COMPILE) -o maybe_test maybe_test.cpp 7 | 8 | test: 9 | ./maybe_test 10 | 11 | clean: 12 | rm -f maybe_test 13 | 14 | all: maybe_test -------------------------------------------------------------------------------- /test/maybe_test.cpp: -------------------------------------------------------------------------------- 1 | #include "maybe.hpp" 2 | 3 | #if defined(__has_feature) && __has_feature(cxx_lambdas) 4 | #define HAS_LAMBDAS 1 5 | #else 6 | #define HAS_LAMBDAS 0 7 | #endif 8 | 9 | struct TrivialType { 10 | TrivialType(int n = 0) : n(n) {} 11 | TrivialType(const TrivialType& other) = default; 12 | TrivialType(TrivialType&& other) = default; 13 | TrivialType& operator=(const TrivialType&) = default; 14 | TrivialType& operator=(TrivialType&&) = default; 15 | int n; 16 | }; 17 | 18 | struct OnlyCopyConstructible { 19 | OnlyCopyConstructible(int n = 0) : n(n) {} 20 | OnlyCopyConstructible(const OnlyCopyConstructible& other) = default; 21 | OnlyCopyConstructible(OnlyCopyConstructible&&) = delete; 22 | OnlyCopyConstructible& operator=(const OnlyCopyConstructible&) = delete; 23 | OnlyCopyConstructible& operator=(OnlyCopyConstructible&&) = delete; 24 | int n; 25 | }; 26 | 27 | struct OnlyMoveConstructible { 28 | OnlyMoveConstructible(int n = 0) : n(n) {} 29 | //OnlyMoveConstructible(const OnlyMoveConstructible&) = delete; 30 | OnlyMoveConstructible(OnlyMoveConstructible&& other) { n = other.n; } 31 | OnlyMoveConstructible& operator=(const OnlyMoveConstructible&) = delete; 32 | OnlyMoveConstructible& operator=(OnlyMoveConstructible&&) = delete; 33 | int n; 34 | }; 35 | 36 | struct OnlyCopyAssignable { 37 | OnlyCopyAssignable(int n = 0) : n(n) {} 38 | OnlyCopyAssignable(const OnlyCopyAssignable& other) = delete; 39 | OnlyCopyAssignable(OnlyCopyAssignable&&) = delete; 40 | OnlyCopyAssignable& operator=(const OnlyCopyAssignable&) = default; 41 | OnlyCopyAssignable& operator=(OnlyCopyAssignable&&) = delete; 42 | int n; 43 | }; 44 | 45 | struct OnlyMoveAssignable { 46 | OnlyMoveAssignable(int n = 0) : n(n) {} 47 | OnlyMoveAssignable(const OnlyMoveAssignable& other) = delete; 48 | OnlyMoveAssignable(OnlyMoveAssignable&&) = delete; 49 | OnlyMoveAssignable& operator=(const OnlyMoveAssignable&) = delete; 50 | OnlyMoveAssignable& operator=(OnlyMoveAssignable&&) = default; 51 | int n; 52 | }; 53 | 54 | struct CopyConstructibleAndAssignable { 55 | CopyConstructibleAndAssignable(int n = 0) : n(n) {} 56 | CopyConstructibleAndAssignable(const CopyConstructibleAndAssignable& other) = default; 57 | CopyConstructibleAndAssignable(CopyConstructibleAndAssignable&&) = delete; 58 | CopyConstructibleAndAssignable& operator=(const CopyConstructibleAndAssignable& other) = default; 59 | CopyConstructibleAndAssignable& operator=(CopyConstructibleAndAssignable&&) = delete; 60 | int n; 61 | }; 62 | 63 | struct MoveConstructibleAndAssignable { 64 | MoveConstructibleAndAssignable(int n = 0) : n(n) {} 65 | MoveConstructibleAndAssignable(const MoveConstructibleAndAssignable& other) = delete; 66 | MoveConstructibleAndAssignable(MoveConstructibleAndAssignable&&) = default; 67 | MoveConstructibleAndAssignable& operator=(const MoveConstructibleAndAssignable& other) = delete; 68 | MoveConstructibleAndAssignable& operator=(MoveConstructibleAndAssignable&&) = default; 69 | int n; 70 | }; 71 | 72 | struct CopyConstructibleAndMoveAssignable { 73 | CopyConstructibleAndMoveAssignable(int n = 0) : n(n) {} 74 | CopyConstructibleAndMoveAssignable(const CopyConstructibleAndMoveAssignable& other) = default; 75 | CopyConstructibleAndMoveAssignable(CopyConstructibleAndMoveAssignable&&) = delete; 76 | CopyConstructibleAndMoveAssignable& operator=(const CopyConstructibleAndMoveAssignable& other) = delete; 77 | CopyConstructibleAndMoveAssignable& operator=(CopyConstructibleAndMoveAssignable&&) = default; 78 | int n; 79 | }; 80 | 81 | struct MoveConstructibleAndCopyAssignable { 82 | MoveConstructibleAndCopyAssignable(int n = 0) : n(n) {} 83 | MoveConstructibleAndCopyAssignable(const MoveConstructibleAndCopyAssignable& other) = delete; 84 | MoveConstructibleAndCopyAssignable(MoveConstructibleAndCopyAssignable&&) = default; 85 | MoveConstructibleAndCopyAssignable& operator=(const MoveConstructibleAndCopyAssignable& other) = default; 86 | MoveConstructibleAndCopyAssignable& operator=(MoveConstructibleAndCopyAssignable&&) = delete; 87 | int n; 88 | }; 89 | 90 | struct CopyAndMoveConstructibleCopyAssignable { 91 | CopyAndMoveConstructibleCopyAssignable(int n = 0) : n(n) {} 92 | CopyAndMoveConstructibleCopyAssignable(const CopyAndMoveConstructibleCopyAssignable& other) = default; 93 | CopyAndMoveConstructibleCopyAssignable(CopyAndMoveConstructibleCopyAssignable&&) = default; 94 | CopyAndMoveConstructibleCopyAssignable& operator=(const CopyAndMoveConstructibleCopyAssignable& other) = default; 95 | CopyAndMoveConstructibleCopyAssignable& operator=(CopyAndMoveConstructibleCopyAssignable&&) = delete; 96 | int n; 97 | }; 98 | 99 | struct CopyAndMoveConstructibleMoveAssignable { 100 | CopyAndMoveConstructibleMoveAssignable(int n = 0) : n(n) {} 101 | CopyAndMoveConstructibleMoveAssignable(const CopyAndMoveConstructibleMoveAssignable& other) = default; 102 | CopyAndMoveConstructibleMoveAssignable(CopyAndMoveConstructibleMoveAssignable&&) = default; 103 | CopyAndMoveConstructibleMoveAssignable& operator=(const CopyAndMoveConstructibleMoveAssignable& other) = delete; 104 | CopyAndMoveConstructibleMoveAssignable& operator=(CopyAndMoveConstructibleMoveAssignable&&) = default; 105 | int n; 106 | }; 107 | 108 | struct CopyConstructibleCopyAndMoveAssignable { 109 | CopyConstructibleCopyAndMoveAssignable(int n = 0) : n(n) {} 110 | CopyConstructibleCopyAndMoveAssignable(const CopyConstructibleCopyAndMoveAssignable& other) = default; 111 | CopyConstructibleCopyAndMoveAssignable(CopyConstructibleCopyAndMoveAssignable&&) = delete; 112 | CopyConstructibleCopyAndMoveAssignable& operator=(const CopyConstructibleCopyAndMoveAssignable& other) = default; 113 | CopyConstructibleCopyAndMoveAssignable& operator=(CopyConstructibleCopyAndMoveAssignable&&) = default; 114 | int n; 115 | }; 116 | 117 | struct MoveConstructibleCopyAndMoveAssignable { 118 | MoveConstructibleCopyAndMoveAssignable(int n = 0) : n(n) {} 119 | MoveConstructibleCopyAndMoveAssignable(const MoveConstructibleCopyAndMoveAssignable& other) = delete; 120 | MoveConstructibleCopyAndMoveAssignable(MoveConstructibleCopyAndMoveAssignable&&) = default; 121 | MoveConstructibleCopyAndMoveAssignable& operator=(const MoveConstructibleCopyAndMoveAssignable& other) = default; 122 | MoveConstructibleCopyAndMoveAssignable& operator=(MoveConstructibleCopyAndMoveAssignable&&) = default; 123 | int n; 124 | }; 125 | 126 | struct NoDefaultConstructor { 127 | explicit NoDefaultConstructor(int n) : n(n) {} 128 | int n; 129 | }; 130 | 131 | 132 | template 133 | void run_test(Maybe& m) { 134 | if (m) exit(1); 135 | T value(123); 136 | m = std::move(value); 137 | if (m.get()->n != 123) exit(1); 138 | m = std::move(value); 139 | if (m.get()->n != 123) exit(1); 140 | } 141 | 142 | static int regular_function_call_count = 0; 143 | 144 | void regular_function_byval(int) { 145 | regular_function_call_count++; 146 | } 147 | 148 | void regular_function_byref(int&) { 149 | regular_function_call_count++; 150 | } 151 | 152 | void regular_function_byconstref(const int&) { 153 | regular_function_call_count++; 154 | } 155 | 156 | void test_maybe_if() { 157 | Maybe yes = 123; 158 | Maybe no; 159 | 160 | #if HAS_LAMBDAS 161 | // Test call with lambda 162 | { 163 | int count = 0; 164 | maybe_if(yes, [&](int it) { ++count; }); 165 | maybe_if(yes, [&](int& it) { ++count; }); 166 | maybe_if(yes, [&](const int& it) { ++count; }); 167 | ASSERT(count == 3); 168 | maybe_if(no, [](int) {}); 169 | maybe_if(no, [](int&) {}); 170 | maybe_if(no, [](const int&) {}); 171 | ASSERT(count == 3); 172 | } 173 | #endif 174 | 175 | // Test call with regular function 176 | { 177 | regular_function_call_count = 0; 178 | maybe_if(yes, regular_function_byval); 179 | maybe_if(yes, regular_function_byref); 180 | maybe_if(yes, regular_function_byconstref); 181 | ASSERT(regular_function_call_count == 3); 182 | maybe_if(no, regular_function_byval); 183 | maybe_if(no, regular_function_byref); 184 | maybe_if(no, regular_function_byconstref); 185 | ASSERT(regular_function_call_count == 3); 186 | } 187 | 188 | // Test call with function object 189 | { 190 | struct ByVal { 191 | int& count; 192 | ByVal(int& count) : count(count) {} 193 | void operator()(int) { count++; } 194 | }; 195 | 196 | struct ByRef { 197 | int& count; 198 | ByRef(int& count) : count(count) {} 199 | void operator()(int&) { count++; } 200 | }; 201 | 202 | struct ByConstRef { 203 | int& count; 204 | ByConstRef(int& count) : count(count) {} 205 | void operator()(const int&) { count++; } 206 | }; 207 | 208 | int count = 0; 209 | maybe_if(yes, ByVal(count)); 210 | maybe_if(yes, ByRef(count)); 211 | maybe_if(yes, ByConstRef(count)); 212 | ASSERT(count == 3); 213 | maybe_if(no, ByVal(count)); 214 | maybe_if(no, ByRef(count)); 215 | maybe_if(no, ByConstRef(count)); 216 | ASSERT(count == 3); 217 | } 218 | 219 | // Test return values 220 | 221 | #if HAS_LAMBDAS 222 | { 223 | Maybe m = maybe_if(yes, [](int) { return 1; }); 224 | ASSERT(m.is_set()); 225 | m = maybe_if(no, [](int) { return 1; }); 226 | ASSERT(!m.is_set()); 227 | } 228 | #endif 229 | 230 | { 231 | struct F { 232 | int operator()(int) { return 1; } 233 | }; 234 | 235 | Maybe m = maybe_if(yes, F()); 236 | ASSERT(m.is_set()); 237 | m = maybe_if(no, F()); 238 | ASSERT(!m.is_set()); 239 | } 240 | 241 | // Test boolean return values for void 242 | 243 | #if HAS_LAMBDAS 244 | { 245 | bool b = yes.map([](int) { }); 246 | ASSERT(b); 247 | b = maybe_if(no, [](int) {}); 248 | ASSERT(!b); 249 | } 250 | #endif 251 | 252 | { 253 | struct F { 254 | void operator()(int) {} 255 | }; 256 | 257 | bool b = yes.map(F()); 258 | ASSERT(b); 259 | b = maybe_if(no, F()); 260 | ASSERT(!b); 261 | } 262 | 263 | // Test non-Maybe option types 264 | { 265 | int n = 123; 266 | int* p = &n; 267 | auto m = maybe_if(p, [](int it) { return it; }); 268 | ASSERT(m); 269 | maybe_if(m, [&](int it) { ASSERT(it == n); }); 270 | p = nullptr; 271 | m = maybe_if(p, [](int it) { return it; }); 272 | ASSERT(!m); 273 | } 274 | } 275 | 276 | int main (int argc, char const *argv[]) 277 | { 278 | { 279 | Maybe m; 280 | m = TrivialType(123); 281 | TrivialType t(123); 282 | m = t; 283 | } 284 | 285 | { 286 | Maybe m(OnlyCopyConstructible(123)); 287 | m = OnlyCopyConstructible(123); 288 | OnlyCopyConstructible v(123); 289 | m = v; 290 | } 291 | 292 | { 293 | Maybe m(OnlyMoveConstructible(123)); 294 | m = OnlyMoveConstructible(123); 295 | OnlyMoveConstructible v(123); 296 | m = std::move(v); 297 | } 298 | 299 | { 300 | Maybe m(OnlyCopyAssignable(123)); 301 | m = OnlyCopyAssignable(123); 302 | OnlyCopyAssignable v(123); 303 | m = v; 304 | } 305 | 306 | { 307 | Maybe m(OnlyMoveAssignable(123)); 308 | m = OnlyMoveAssignable(123); 309 | OnlyMoveAssignable v(123); 310 | m = std::move(v); 311 | } 312 | 313 | { 314 | Maybe m(CopyConstructibleAndAssignable(123)); 315 | m = CopyConstructibleAndAssignable(123); 316 | CopyConstructibleAndAssignable v(123); 317 | m = v; 318 | } 319 | 320 | { 321 | Maybe m(MoveConstructibleAndAssignable(123)); 322 | m = MoveConstructibleAndAssignable(123); 323 | MoveConstructibleAndAssignable v(123); 324 | m = std::move(v); 325 | } 326 | 327 | { 328 | Maybe m(CopyConstructibleAndMoveAssignable(123)); 329 | m = CopyConstructibleAndMoveAssignable(123); 330 | CopyConstructibleAndMoveAssignable v(123); 331 | m = std::move(v); 332 | } 333 | 334 | { 335 | Maybe m(MoveConstructibleAndCopyAssignable(123)); 336 | m = MoveConstructibleAndCopyAssignable(123); 337 | MoveConstructibleAndCopyAssignable v(123); 338 | m = v; 339 | } 340 | 341 | { 342 | Maybe m(CopyAndMoveConstructibleCopyAssignable(123)); 343 | CopyAndMoveConstructibleCopyAssignable v(123); 344 | Maybe m2(v); 345 | Maybe m3(std::move(v)); 346 | m = v; 347 | } 348 | 349 | { 350 | Maybe m(CopyAndMoveConstructibleMoveAssignable(123)); 351 | CopyAndMoveConstructibleMoveAssignable v(123); 352 | m = std::move(v); 353 | Maybe m2 = v; 354 | } 355 | 356 | { 357 | CopyConstructibleCopyAndMoveAssignable v(123); 358 | Maybe m(v); 359 | m = std::move(v); 360 | } 361 | 362 | { 363 | MoveConstructibleCopyAndMoveAssignable v(123); 364 | Maybe m(std::move(v)); 365 | m = std::move(v); 366 | m = v; 367 | } 368 | 369 | { 370 | Maybe m = NoDefaultConstructor(123); 371 | m = NoDefaultConstructor(123); 372 | NoDefaultConstructor v(456); 373 | m = v; 374 | } 375 | 376 | test_maybe_if(); 377 | 378 | return 0; 379 | } -------------------------------------------------------------------------------- /type/attribute.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ATTRIBUTE_HPP_FFQKLYB6 3 | #define ATTRIBUTE_HPP_FFQKLYB6 4 | 5 | #include "object/object.hpp" 6 | #include "type/type.hpp" 7 | #include "serialization/archive.hpp" 8 | 9 | struct AttributeBase { 10 | AttributeBase(std::string name, std::string description) : name_(std::move(name)), description_(std::move(description)) {} 11 | virtual ~AttributeBase() {} 12 | 13 | virtual const Type* type() const = 0; 14 | const std::string& name() const { return name_; } 15 | const std::string& description() const { return description_; } 16 | protected: 17 | std::string name_; 18 | std::string description_; 19 | }; 20 | 21 | template 22 | struct Attribute : AttributeBase { 23 | Attribute(std::string name, std::string description) : AttributeBase(std::move(name), std::move(description)) {} 24 | const Type* type() const override { return get_type(); } 25 | }; 26 | 27 | template 28 | struct AttributeForObject { 29 | virtual ~AttributeForObject() {} 30 | virtual const Type* attribute_type() const = 0; 31 | virtual const std::string& attribute_name() const = 0; 32 | virtual const std::string& attribute_description() const = 0; 33 | virtual bool deserialize_attribute(T* object, const ArchiveNode&, IUniverse&) const = 0; 34 | virtual bool serialize_attribute(const T* object, ArchiveNode&, IUniverse&) const = 0; 35 | }; 36 | 37 | template 38 | struct AttributeForObjectOfType : AttributeForObject, Attribute { 39 | AttributeForObjectOfType(std::string name, std::string description) : Attribute(name, description) {} 40 | 41 | virtual GetterType get(const ObjectType&) const = 0; 42 | virtual void set(ObjectType&, MemberType value) const = 0; 43 | 44 | const Type* attribute_type() const { return get_type(); } 45 | const std::string& attribute_name() const { return this->name_; } 46 | const std::string& attribute_description() const { return this->description_; } 47 | 48 | bool deserialize_attribute(ObjectType* object, const ArchiveNode& node, IUniverse& universe) const { 49 | MemberType value; 50 | this->type()->deserialize(reinterpret_cast(&value), node, universe); 51 | set(*object, std::move(value)); 52 | return true; // eh... 53 | } 54 | 55 | bool serialize_attribute(const ObjectType* object, ArchiveNode& node, IUniverse& universe) const { 56 | GetterType value = get(*object); 57 | this->type()->serialize(reinterpret_cast(&value), node, universe); 58 | return true; // eh... 59 | } 60 | }; 61 | 62 | template 63 | struct MemberAttribute : AttributeForObjectOfType { 64 | typedef MemberType ObjectType::* MemberPointer; 65 | 66 | MemberAttribute(std::string name, std::string description, MemberPointer member) : AttributeForObjectOfType(name, description), member_(member) {} 67 | 68 | const MemberType& get(const ObjectType& object) const { 69 | return object.*member_; 70 | } 71 | 72 | void set(ObjectType& object, MemberType value) const { 73 | object.*member_ = std::move(value); 74 | } 75 | 76 | // override deserialize_attribute so we can deserialize in-place 77 | bool deserialize_attribute(ObjectType* object, const ArchiveNode& node, IUniverse& universe) const { 78 | MemberType* ptr = &(object->*member_); 79 | this->type()->deserialize(reinterpret_cast(ptr), node, universe); 80 | return true; // eh... 81 | } 82 | 83 | MemberPointer member_; 84 | }; 85 | 86 | template 91 | struct MethodAttribute : AttributeForObjectOfType { 92 | typedef GetterReturnType(ObjectType::*GetterPointer)() const; 93 | typedef SetterReturnTypeUnused(ObjectType::*SetterPointer)(SetterArgumentType); 94 | 95 | MethodAttribute(std::string name, std::string description, GetterPointer getter, SetterPointer setter) : AttributeForObjectOfType(name, description), getter_(getter), setter_(setter) {} 96 | 97 | GetterReturnType get(const ObjectType& object) const { 98 | return (object.*getter_)(); 99 | } 100 | 101 | void set(ObjectType& object, MemberType value) const { 102 | (object.*setter_)(std::move(value)); 103 | } 104 | 105 | GetterPointer getter_; 106 | SetterPointer setter_; 107 | }; 108 | 109 | #endif /* end of include guard: ATTRIBUTE_HPP_FFQKLYB6 */ 110 | -------------------------------------------------------------------------------- /type/reference_type.cpp: -------------------------------------------------------------------------------- 1 | #include "reference_type.hpp" 2 | #include 3 | 4 | std::string ReferenceType::build_reference_type_name(std::string base_name, const Type* pointee) { 5 | std::stringstream ss; 6 | ss << base_name << '<' << pointee->name() << '>'; 7 | return ss.str(); 8 | } -------------------------------------------------------------------------------- /type/reference_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef REFERENCE_TYPE_HPP_EAHSMBCU 3 | #define REFERENCE_TYPE_HPP_EAHSMBCU 4 | 5 | #include "type/type.hpp" 6 | #include "serialization/archive_node.hpp" 7 | 8 | struct ReferenceType : Type { 9 | ReferenceType(std::string name) : name_(std::move(name)) {} 10 | 11 | virtual const Type* pointee_type() const = 0; 12 | 13 | const std::string& name() const override { return name_; } 14 | protected: 15 | static std::string build_reference_type_name(std::string base_name, const Type* pointee); 16 | private: 17 | std::string name_; 18 | }; 19 | 20 | template 21 | struct ReferenceTypeImpl : TypeFor { 22 | typedef typename T::PointeeType PointeeType; 23 | 24 | ReferenceTypeImpl(std::string base_name) : TypeFor(ReferenceType::build_reference_type_name(base_name, get_type())) {} 25 | 26 | // ReferenceType interface 27 | const Type* pointee_type() const { return get_type(); } 28 | 29 | // Type interface 30 | void deserialize(T& ptr, const ArchiveNode& node, IUniverse&) const; 31 | void serialize(const T& ptr, ArchiveNode& node, IUniverse&) const; 32 | }; 33 | 34 | template 35 | void ReferenceTypeImpl::deserialize(T& ptr, const ArchiveNode& node, IUniverse&) const { 36 | node.register_reference_for_deserialization(ptr); 37 | } 38 | 39 | template 40 | void ReferenceTypeImpl::serialize(const T& ptr, ArchiveNode& node, IUniverse&) const { 41 | node.register_reference_for_serialization(ptr); 42 | } 43 | 44 | #endif /* end of include guard: REFERENCE_TYPE_HPP_EAHSMBCU */ 45 | -------------------------------------------------------------------------------- /type/type.cpp: -------------------------------------------------------------------------------- 1 | #include "type/type.hpp" 2 | #include "serialization/archive_node.hpp" 3 | #include 4 | 5 | #define DEFINE_SIMPLE_TYPE(T, IS_FLOAT, IS_SIGNED) template <> const Type* build_type_info() { \ 6 | if (IS_FLOAT) { \ 7 | static const FloatType type(#T, sizeof(T)); \ 8 | return &type; \ 9 | } else { \ 10 | static const IntegerType type(#T, sizeof(T), IS_SIGNED); \ 11 | return &type; \ 12 | } \ 13 | } 14 | 15 | DEFINE_SIMPLE_TYPE(int8, false, true) 16 | DEFINE_SIMPLE_TYPE(int16, false, true) 17 | DEFINE_SIMPLE_TYPE(int32, false, true) 18 | DEFINE_SIMPLE_TYPE(int64, false, true) 19 | DEFINE_SIMPLE_TYPE(uint8, false, false) 20 | DEFINE_SIMPLE_TYPE(uint16, false, false) 21 | DEFINE_SIMPLE_TYPE(uint32, false, false) 22 | DEFINE_SIMPLE_TYPE(uint64, false, false) 23 | DEFINE_SIMPLE_TYPE(float32, true, true) 24 | DEFINE_SIMPLE_TYPE(float64, true, true) 25 | 26 | 27 | void IntegerType::deserialize(byte* place, const ArchiveNode& node, IUniverse&) const { 28 | if (is_signed_) { 29 | switch (width_) { 30 | case 1: node.get(*reinterpret_cast(place)); return; 31 | case 2: node.get(*reinterpret_cast(place)); return; 32 | case 4: node.get(*reinterpret_cast(place)); return; 33 | case 8: node.get(*reinterpret_cast(place)); return; 34 | default: ASSERT(false); // non-standard integer size 35 | } 36 | } else { 37 | switch (width_) { 38 | case 1: node.get(*reinterpret_cast(place)); return; 39 | case 2: node.get(*reinterpret_cast(place)); return; 40 | case 4: node.get(*reinterpret_cast(place)); return; 41 | case 8: node.get(*reinterpret_cast(place)); return; 42 | default: ASSERT(false); // non-standard integer size 43 | } 44 | } 45 | } 46 | 47 | void IntegerType::serialize(const byte* place, ArchiveNode& node, IUniverse&) const { 48 | if (is_signed_) { 49 | switch (width_) { 50 | case 1: node.set(*reinterpret_cast(place)); return; 51 | case 2: node.set(*reinterpret_cast(place)); return; 52 | case 4: node.set(*reinterpret_cast(place)); return; 53 | case 8: node.set(*reinterpret_cast(place)); return; 54 | default: ASSERT(false); // non-standard integer size 55 | } 56 | } else { 57 | switch (width_) { 58 | case 1: node.set(*reinterpret_cast(place)); return; 59 | case 2: node.set(*reinterpret_cast(place)); return; 60 | case 4: node.set(*reinterpret_cast(place)); return; 61 | case 8: node.set(*reinterpret_cast(place)); return; 62 | default: ASSERT(false); // non-standard integer size 63 | } 64 | } 65 | } 66 | 67 | void* IntegerType::cast(const SimpleType* to, void* memory) const { 68 | if (to == this) return memory; 69 | 70 | auto enum_type = dynamic_cast(to); 71 | if (enum_type != nullptr && enum_type->size() <= width_) { 72 | if (is_signed_) { 73 | ASSERT(width_ <= sizeof(ssize_t)); 74 | ssize_t n = 0; 75 | memcpy(&n, memory, width_); 76 | if (enum_type->contains(n)) 77 | return memory; 78 | } else { 79 | ASSERT(width_ <= sizeof(size_t)); 80 | size_t n = 0; 81 | memcpy(&n, memory, width_); 82 | if (enum_type->contains(n)) 83 | return memory; 84 | } 85 | } 86 | 87 | return nullptr; 88 | } 89 | 90 | void FloatType::deserialize(byte* place, const ArchiveNode& node, IUniverse&) const { 91 | if (width_ == 4) { 92 | node.get(*reinterpret_cast(place)); 93 | } else if (width_ == 8) { 94 | node.get(*reinterpret_cast(place)); 95 | } 96 | ASSERT(false); // FloatType with neither 32-bit nor 64-bit floats? 97 | } 98 | 99 | void FloatType::serialize(const byte* place, ArchiveNode& node, IUniverse&) const { 100 | if (width_ == 4) { 101 | node.set(*reinterpret_cast(place)); 102 | } else if (width_ == 8) { 103 | node.set(*reinterpret_cast(place)); 104 | } 105 | ASSERT(false); // FloatType with neither 32-bit nor 64-bit floats? 106 | } 107 | 108 | bool EnumType::contains(ssize_t value) const { 109 | if (value >= min() && value <= max()) { 110 | for (auto& tuple: entries_) { 111 | if (std::get<1>(tuple) == value) 112 | return true; 113 | } 114 | } 115 | return false; 116 | } 117 | 118 | bool EnumType::name_for_value(ssize_t value, std::string& name) const { 119 | if (value >= min() && value <= max()) { 120 | for (auto& tuple: entries_) { 121 | if (std::get<1>(tuple) == value) { 122 | name = std::get<0>(tuple); 123 | return true; 124 | } 125 | } 126 | } 127 | return false; 128 | } 129 | 130 | bool EnumType::value_for_name(const std::string& name, ssize_t& out_value) const { 131 | for (auto& tuple: entries_) { 132 | if (std::get<0>(tuple) == name) { 133 | out_value = std::get<1>(tuple); 134 | return true; 135 | } 136 | } 137 | return false; 138 | } 139 | 140 | void EnumType::deserialize(byte* place, const ArchiveNode& node, IUniverse&) const { 141 | std::string name; 142 | if (node.get(name)) { 143 | ssize_t value; 144 | ASSERT(width_ <= sizeof(ssize_t)); 145 | if (value_for_name(name, value)) { 146 | memcpy(place, &value, width_); 147 | // Success! 148 | } else { 149 | // XXX: Invalid enum entry. 150 | } 151 | } else { 152 | // XXX: Invalid node (not a string) 153 | } 154 | } 155 | 156 | void EnumType::serialize(const byte* place, ArchiveNode& node, IUniverse&) const { 157 | ssize_t value = 0; 158 | ASSERT(width_ <= sizeof(ssize_t)); 159 | memcpy(&value, place, width_); 160 | std::string name; 161 | if (name_for_value(value, name)) { 162 | node.set(name); 163 | // Success! 164 | } else { 165 | // XXX: Invalid enum entry! 166 | } 167 | } 168 | 169 | void* EnumType::cast(const SimpleType* to, void* memory) const { 170 | if (to == this) return memory; 171 | 172 | auto integer_type = dynamic_cast(to); 173 | if (integer_type != nullptr && integer_type->size() <= width_) { 174 | if (integer_type->is_signed()) { 175 | ssize_t value = 0; 176 | ASSERT(integer_type->size() <= sizeof(ssize_t)); 177 | memcpy(&value, memory, integer_type->size()); 178 | if (contains(value)) { 179 | return memory; 180 | } 181 | } else { 182 | size_t value = 0; 183 | ASSERT(integer_type->size() <= sizeof(size_t)); 184 | memcpy(&value, memory, integer_type->size()); 185 | if (contains(value)) { 186 | return memory; 187 | } 188 | } 189 | } 190 | 191 | return nullptr; 192 | } 193 | 194 | void* FloatType::cast(const SimpleType* to, void* memory) const { 195 | if (to == this) return memory; 196 | return nullptr; 197 | } 198 | 199 | const std::string VoidType::Name = "void"; 200 | 201 | const VoidType* VoidType::get() { 202 | static const VoidType p; 203 | return &p; 204 | } 205 | 206 | template <> const Type* build_type_info() { 207 | return VoidType::get(); 208 | } 209 | 210 | 211 | void StringType::deserialize(std::string& place, const ArchiveNode& node, IUniverse&) const { 212 | node.get(place); 213 | } 214 | 215 | void StringType::serialize(const std::string& place, ArchiveNode& node, IUniverse&) const { 216 | node.set(place); 217 | } 218 | 219 | const StringType* StringType::get() { 220 | static const StringType type = StringType(); 221 | return &type; 222 | } 223 | 224 | const std::string& StringType::name() const { 225 | static const std::string name = "std::string"; 226 | return name; 227 | } 228 | -------------------------------------------------------------------------------- /type/type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef TYPE_HPP_ZXLGBWRF 3 | #define TYPE_HPP_ZXLGBWRF 4 | 5 | #include "base/basic.hpp" 6 | #include "base/array.hpp" 7 | #include "object/object.hpp" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | struct ArchiveNode; 15 | struct IUniverse; 16 | struct SlotAttributeBase; 17 | 18 | struct Type { 19 | virtual void deserialize(byte* place, const ArchiveNode&, IUniverse&) const = 0; 20 | virtual void serialize(const byte* place, ArchiveNode&, IUniverse&) const = 0; 21 | virtual void construct(byte* place, IUniverse&) const = 0; 22 | virtual void destruct(byte* place, IUniverse&) const = 0; 23 | 24 | virtual const std::string& name() const = 0; 25 | virtual size_t size() const = 0; 26 | virtual bool is_abstract() const { return false; } 27 | protected: 28 | Type() {} 29 | }; 30 | 31 | template 32 | struct TypeFor : TypeType { 33 | // Forwarding constructor. 34 | template 35 | TypeFor(Args&&... args) : TypeType(std::forward(args)...) {} 36 | 37 | // Override interface. 38 | virtual void deserialize(ObjectType& place, const ArchiveNode&, IUniverse&) const = 0; 39 | virtual void serialize(const ObjectType& place, ArchiveNode&, IUniverse&) const = 0; 40 | 41 | 42 | // Do not override. 43 | void deserialize(byte* place, const ArchiveNode& node, IUniverse& universe) const { 44 | this->deserialize(*reinterpret_cast(place), node, universe); 45 | } 46 | void serialize(const byte* place, ArchiveNode& node, IUniverse& universe) const { 47 | this->serialize(*reinterpret_cast(place), node, universe); 48 | } 49 | void construct(byte* place, IUniverse&) const { 50 | ::new(place) ObjectType; 51 | } 52 | void destruct(byte* place, IUniverse&) const { 53 | reinterpret_cast(place)->~ObjectType(); 54 | } 55 | size_t size() const { return sizeof(ObjectType); } 56 | }; 57 | 58 | struct VoidType : Type { 59 | static const VoidType* get(); 60 | 61 | void deserialize(byte* place, const ArchiveNode&, IUniverse&) const override {} 62 | void serialize(const byte*, ArchiveNode&, IUniverse&) const override {} 63 | virtual void construct(byte*, IUniverse&) const override {} 64 | virtual void destruct(byte*, IUniverse&) const override {} 65 | static const std::string Name; 66 | const std::string& name() const override { return Name; } 67 | size_t size() const override { return 0; } 68 | bool is_abstract() const override { return true; } 69 | private: 70 | VoidType() {} 71 | }; 72 | 73 | struct SimpleType : Type { 74 | SimpleType(std::string name, size_t width, size_t component_width, bool is_float, bool is_signed) : name_(std::move(name)), width_(width), component_width_(component_width), is_float_(is_float), is_signed_(is_signed) {} 75 | const std::string& name() const override { return name_; } 76 | void construct(byte* place, IUniverse&) const { std::fill(place, place + size(), 0); } 77 | void destruct(byte*, IUniverse&) const {} 78 | 79 | size_t size() const override { return width_; } 80 | size_t num_components() const { return width_ / component_width_; } 81 | bool is_signed() const { return is_signed_; } 82 | virtual void* cast(const SimpleType* to, void* o) const = 0; 83 | protected: 84 | std::string name_; 85 | size_t width_; 86 | size_t component_width_; 87 | bool is_float_; 88 | bool is_signed_; 89 | }; 90 | 91 | struct EnumType : SimpleType { 92 | EnumType(std::string name, size_t width, bool is_signed = true) : SimpleType(name, width, width, false, is_signed), max_(1LL-SSIZE_MAX), min_(SSIZE_MAX) {} 93 | void add_entry(std::string name, ssize_t value, std::string description) { 94 | entries_.emplace_back(std::make_tuple(std::move(name), value, std::move(description))); 95 | } 96 | bool contains(ssize_t value) const; 97 | ssize_t max() const { return max_; } 98 | ssize_t min() const { return min_; } 99 | bool name_for_value(ssize_t value, std::string& out_name) const; 100 | bool value_for_name(const std::string& name, ssize_t& out_value) const; 101 | 102 | void deserialize(byte*, const ArchiveNode&, IUniverse&) const override; 103 | void serialize(const byte*, ArchiveNode&, IUniverse&) const override; 104 | void* cast(const SimpleType* to, void* o) const; 105 | private: 106 | Array> entries_; 107 | ssize_t max_; 108 | ssize_t min_; 109 | }; 110 | 111 | struct IntegerType : SimpleType { 112 | IntegerType(std::string name, size_t width, bool is_signed = true) : SimpleType(name, width, width, false, is_signed) {} 113 | void deserialize(byte*, const ArchiveNode&, IUniverse&) const override; 114 | void serialize(const byte*, ArchiveNode&, IUniverse&) const override; 115 | void* cast(const SimpleType* to, void* o) const; 116 | size_t max() const; 117 | ssize_t min() const; 118 | }; 119 | 120 | struct FloatType : SimpleType { 121 | FloatType(std::string name, size_t width) : SimpleType(name, width, width, true, true) {} 122 | void deserialize(byte*, const ArchiveNode& node, IUniverse&) const override; 123 | void serialize(const byte*, ArchiveNode&, IUniverse&) const override; 124 | void* cast(const SimpleType* to, void* o) const; 125 | }; 126 | 127 | struct VectorType : SimpleType { 128 | VectorType(std::string name, size_t width, size_t component_width, bool is_float, bool is_signed = true) : SimpleType(name, width, component_width, is_float, is_signed) {} 129 | void deserialize(byte*, const ArchiveNode&, IUniverse&) const override; 130 | void serialize(const byte*, ArchiveNode&, IUniverse&) const override; 131 | void* cast(const SimpleType* to, void* o) const; 132 | }; 133 | 134 | struct StringType : TypeFor { 135 | static const StringType* get(); 136 | 137 | void deserialize(std::string& place, const ArchiveNode&, IUniverse&) const override; 138 | void serialize(const std::string& place, ArchiveNode&, IUniverse&) const override; 139 | 140 | const std::string& name() const override; 141 | size_t size() const override { return sizeof(std::string); } 142 | }; 143 | 144 | struct DerivedType : Type { 145 | virtual Object* cast(const DerivedType* to, Object* o) const = 0; 146 | virtual const SlotAttributeBase* get_slot_by_name(const std::string& name) const { return nullptr; } 147 | }; 148 | 149 | // static_cast 150 | template 151 | typename std::enable_if::value, To*>::type 152 | aspect_cast(From* ptr) { 153 | return ptr; 154 | } 155 | 156 | // complex cast 157 | template 158 | typename std::enable_if::value, To*>::type 159 | aspect_cast(From* ptr) { 160 | Object* o = ptr; // check that From derives from Object. 161 | const Type* from = o->object_type(); 162 | const Type* to = get_type(); 163 | 164 | auto derived_type = dynamic_cast(from); 165 | if (derived_type != nullptr) { 166 | auto other_derived_type = dynamic_cast(to); 167 | if (other_derived_type != nullptr) { 168 | return dynamic_cast(derived_type->cast(other_derived_type, ptr)); 169 | } 170 | return nullptr; 171 | } 172 | 173 | auto simple_type = dynamic_cast(from); 174 | if (simple_type != nullptr) { 175 | auto other_simple_type = dynamic_cast(to); 176 | if (other_simple_type != nullptr) { 177 | return static_cast(simple_type->cast(other_simple_type, ptr)); 178 | } 179 | return nullptr; 180 | } 181 | 182 | return nullptr; 183 | } 184 | 185 | // runtime cast 186 | template 187 | Object* 188 | aspect_cast(From* ptr, const DerivedType* to) { 189 | Object* o = ptr; // check that From derives from Object. 190 | return o->object_type()->cast(to, o); 191 | } 192 | 193 | /*template 194 | const Object* 195 | aspect_cast(const From* ptr, const DerivedType* to) { 196 | const Object* o = ptr; 197 | return o->object_type()->cast(to, o); 198 | }*/ 199 | 200 | #define DECLARE_TYPE(T) template<> const Type* build_type_info(); 201 | DECLARE_TYPE(int8) 202 | DECLARE_TYPE(int16) 203 | DECLARE_TYPE(int32) 204 | DECLARE_TYPE(int64) 205 | DECLARE_TYPE(uint8) 206 | DECLARE_TYPE(uint16) 207 | DECLARE_TYPE(uint32) 208 | DECLARE_TYPE(uint64) 209 | DECLARE_TYPE(float32) 210 | DECLARE_TYPE(float64) 211 | 212 | template struct BuildTypeInfo {}; 213 | 214 | template <> struct BuildTypeInfo { 215 | static const VoidType* build() { return VoidType::get(); } 216 | }; 217 | 218 | template <> struct BuildTypeInfo { 219 | static const StringType* build() { return StringType::get(); } 220 | }; 221 | 222 | template const Type* build_type_info() { 223 | return BuildTypeInfo::build(); 224 | } 225 | 226 | 227 | template 228 | void append_type_names(std::ostream& os) { 229 | const Type* t = get_type(); 230 | os << t->name(); 231 | os << ", "; 232 | append_type_names(); 233 | } 234 | 235 | template 236 | void append_type_names(std::ostream& os) { 237 | const Type* t = get_type(); 238 | os << t->name(); 239 | } 240 | 241 | template 242 | std::string get_signature_description() { 243 | std::stringstream ss; 244 | ss << '('; 245 | append_type_names(ss); 246 | ss << ')'; 247 | return ss.str(); 248 | } 249 | 250 | 251 | template 252 | void build_signature(Array& signature) { 253 | signature.push_back(get_type()); 254 | } 255 | template 256 | void build_signature(Array& signature) { 257 | signature.push_back(get_type()); 258 | build_signature(signature); 259 | } 260 | 261 | #endif /* end of include guard: TYPE_HPP_ZXLGBWRF */ 262 | -------------------------------------------------------------------------------- /type/type_registry.cpp: -------------------------------------------------------------------------------- 1 | #include "type/type_registry.hpp" 2 | #include "object/struct_type.hpp" 3 | #include "base/basic.hpp" 4 | #include 5 | 6 | struct TypeRegistry::Impl { 7 | std::map type_map; 8 | }; 9 | 10 | TypeRegistry::Impl* TypeRegistry::impl() { 11 | static Impl* i = new Impl; 12 | return i; 13 | } 14 | 15 | void TypeRegistry::add(const ObjectTypeBase* type) { 16 | impl()->type_map[type->name()] = type; 17 | } 18 | 19 | const ObjectTypeBase* TypeRegistry::get(const std::string& name) { 20 | return find_or(impl()->type_map, name, nullptr); 21 | } -------------------------------------------------------------------------------- /type/type_registry.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef TYPE_REGISTRY_HPP_LPQGF8DT 3 | #define TYPE_REGISTRY_HPP_LPQGF8DT 4 | 5 | #include "object/object.hpp" 6 | #include 7 | 8 | class TypeRegistry { 9 | public: 10 | template 11 | static void add(); 12 | static void add(const ObjectTypeBase* type); 13 | 14 | static const ObjectTypeBase* get(const std::string& name); 15 | private: 16 | TypeRegistry(); 17 | struct Impl; 18 | static Impl* impl(); 19 | }; 20 | 21 | template 22 | void TypeRegistry::add() { 23 | add(get_type()); 24 | } 25 | 26 | 27 | #endif /* end of include guard: TYPE_REGISTRY_HPP_LPQGF8DT */ 28 | --------------------------------------------------------------------------------