#ifndef ComponentWrapper_h__ #define ComponentWrapper_h__ #include #include #include "../Common.h" #include "Entity.h" #include "ComponentInfo.h" #include "Util/Any.h" template struct ComponentField { }; template struct ComponentField::value>::type> { static T& Get(const ComponentInfo::Field_t& info, char* data) { return *reinterpret_cast(data); } static void Set(const ComponentInfo::Field_t& info, char* data, const T& value) { Get(data) = value; } }; template <> struct ComponentField { static std::string& Get(const ComponentInfo::Field_t& info, char* data) { return **reinterpret_cast(data); } static void Set(const ComponentInfo::Field_t& info, char* data, const std::string& value) { Get(info, data) = value; } }; struct ComponentWrapper { ComponentWrapper(const ComponentInfo& componentInfo, char* data) : Info(componentInfo) , EntityID(*reinterpret_cast<::EntityID*>(data)) , Data(data + sizeof(::EntityID)) { } const ComponentInfo& Info; const ::EntityID EntityID; char* Data; ComponentInfo::EnumType Enum(const char* fieldName, const char* enumKey) { return Info.Meta->FieldEnumDefinitions.at(fieldName).at(enumKey); } template T& Field(std::string name) { const ComponentInfo::Field_t& field = Info.Fields.at(name); if (sizeof(T) > field.Stride) { std::stringstream message; message << "Type size of \"" << typeid(T).name() << "\" doesn't match size of component field \"" << Info.Name << "." << name << "\"!"; throw new std::runtime_error(message.str().c_str()); } return *reinterpret_cast(&Data[field.Offset]); } template void SetField(std::string name, const T value) { Field(name) = value; } //template //void SetField(std::string name, T& value) { Field(name) = value; } // Specialization for string literals template void SetField(std::string name, const char(&value)[N]) { Field(name) = std::string(value); } void Copy(ComponentWrapper& destination) { // Copy trivial data memcpy(destination.Data, Data, Info.Stride); // Duplicate strings SolidifyStrings(destination); } // When component data has been copied, strings need to be reconstructed or they'll refer to the same data! static void SolidifyStrings(ComponentWrapper& component) { for (auto& name : component.Info.StringFields) { std::size_t offset = component.Info.Fields.at(name).Offset; std::string value = *reinterpret_cast(component.Data + offset); new (component.Data + offset) std::string(value); } } // This needs to be called to properly free component data, because strings. static void Destroy(ComponentInfo info, char* data) { // Call std::string destructors for (auto& name : info.StringFields) { std::size_t offset = info.Fields.at(name).Offset; auto field = reinterpret_cast(data + offset); field->~basic_string(); } } struct SubscriptProxy { friend struct ComponentWrapper; private: SubscriptProxy(ComponentWrapper* component, std::string propertyName) : m_Component(component) , m_PropertyName(propertyName) { } ComponentWrapper* m_Component; std::string m_PropertyName; public: // Return the integer value of an enum type key for this field ComponentInfo::EnumType Enum(const char* enumKey) { return m_Component->Enum(m_PropertyName.c_str(), enumKey); } template operator T&() { return m_Component->Field(m_PropertyName); } template void operator=(const T val) { m_Component->SetField(m_PropertyName, val); } // TODO: Pass by reference and rvalue (universal reference?) //template //void operator=(T& val) { m_Component->SetField(m_PropertyName, val); } // Specialization for string literals template void operator=(const char(&val)[N]) { m_Component->SetField(m_PropertyName, val); } }; SubscriptProxy operator[](std::string propertyName) { return SubscriptProxy(this, propertyName); } }; // A component wrapper that "owns" its data through a shared pointer struct SharedComponentWrapper : ComponentWrapper { SharedComponentWrapper(const ComponentInfo& componentInfo, boost::shared_array data) : ComponentWrapper(componentInfo, data.get()) , m_DataReference(data) { } private: boost::shared_array m_DataReference; }; // TODO: Move this to Tests once entity importing is finished class ComponentWrapperFactory { public: ComponentWrapperFactory() = default; ComponentWrapperFactory(std::string componentTypeName, unsigned int allocation = 0) { m_ComponentInfo.Name = componentTypeName; m_ComponentInfo.Meta = std::make_shared(); m_ComponentInfo.Meta->Allocation = allocation; } template void AddProperty(std::string fieldName, T defaultValue) { auto& field = m_ComponentInfo.Fields[fieldName]; field.Name = fieldName; field.Type = typeid(T).name(); field.Offset = m_ComponentInfo.Stride; field.Stride = sizeof(T); m_ComponentInfo.FieldsInOrder.push_back(field.Name); if (field.Type == typeid(std::string).name()) { field.Type = "string"; m_ComponentInfo.StringFields.push_back(field.Name); } m_ComponentInfo.Stride += sizeof(T); m_DefaultValues.push_back(std::make_pair(field, defaultValue)); } ComponentInfo& Finalize() { m_ComponentInfo.Defaults = boost::shared_array(new char[m_ComponentInfo.Stride]); std::size_t offset = 0; for (auto& pair : m_DefaultValues) { if (pair.first.Type == "string") { new (m_ComponentInfo.Defaults.get() + offset) std::string(*reinterpret_cast(pair.second.Data.get())); } else { memcpy(m_ComponentInfo.Defaults.get() + offset, pair.second.Data.get(), pair.second.Size); } offset += pair.second.Size; } return m_ComponentInfo; } operator ComponentInfo&() { return Finalize(); } private: ComponentInfo m_ComponentInfo; std::vector> m_DefaultValues; }; #endif