#pragma once #include "reflection/Type.h" #include "util/utilities.h" #include "util/math.h" #include #include #include namespace RBX { namespace Reflection { class EnumDescriptor : public Type { public: static std::vector< const EnumDescriptor* >::const_iterator enumsBegin(); static std::vector< const EnumDescriptor* >::const_iterator enumsEnd(); static size_t allEnumSize() {return allEnums().size();} class Item : public Descriptor { public: const EnumDescriptor& owner; const int value; // value of the enum const size_t index; // place in ordered enum values (0<=index EnumNameTable; #else typedef std::map EnumNameTable; #endif private: static EnumNameTable& allEnumsNameLookup(); static std::vector& allEnums(); static bool equalValue(const Item* item, int intValue) { return item->value == intValue; } static int count; protected: std::vector< const Item* > allItems; size_t enumCount; size_t enumCountMSB; EnumDescriptor(const char* typeName); ~EnumDescriptor(); public: size_t getEnumCount() const { return enumCount; } size_t getEnumCountMSB() const { return enumCountMSB; } std::vector< const Item* >::const_iterator begin() const { return allItems.begin(); } std::vector< const Item* >::const_iterator end() const { return allItems.end(); } static const EnumDescriptor* lookupDescriptor(const RBX::Name& name) { EnumNameTable::const_iterator iter = allEnumsNameLookup().find(&name); if (iter!=allEnumsNameLookup().end()) return iter->second; else return NULL; } static const EnumDescriptor* lookupDescriptor(const Type& type) { if (type.isEnum) return static_cast(&type); else return NULL; } bool isValue(int intValue) const { return std::find_if(allItems.begin(), allItems.end(), boost::bind(&equalValue, _1, intValue)) != allItems.end(); } virtual const Item* lookup(const char* text) const = 0; virtual const Item* lookup(const Variant& value) const = 0; virtual bool convertToValue(size_t index, Variant& value) const = 0; virtual bool convertToString(size_t index, std::string& value) const = 0; }; // A thread-safe singleton! template class Singleton : boost::noncopyable { static T& doGetSingleton() { static T s; return s; } static void initSingleton() { doGetSingleton(); } public: static T& singleton() { static boost::once_flag flag = BOOST_ONCE_INIT; boost::call_once(&initSingleton, flag); return doGetSingleton(); }; }; template class EnumRegistrar; template class EnumDesc : public EnumDescriptor { public: friend class Singleton >; static const EnumDesc& singleton() { return Singleton >::singleton(); } private: // You must implement the following constructor for each EnumDesc that you define EnumDesc(); ~EnumDesc() { // Force linking of EnumRegistrar, which will force clients // of this library to define EnumRegistrar::registrar in // their startup code. Reflection::EnumRegistrar::registrar.dummy(); std::for_each(allItems.begin(), allItems.end(), &del_fun); } std::map nameToEnum; std::map nameToEnumLegacy; std::vector< const RBX::Name* > enumToName; // maps enum to Name (there may be gaps) std::vector< std::string > enumToString; // maps enum to String (there may be gaps) std::vector< const Item* > enumToItem; // maps enum to Item (there may be gaps) std::vector< Enum > intToEnum; // maps legacy values to proper enum std::vector< Enum > indexToEnum; std::vector< size_t > enumToIndex; // maps enum to Index (there may be gaps) // Used in constructor void addPair(Enum value, const char* name, Descriptor::Attributes attributes = Descriptor::Attributes()) { RBXASSERT_VERY_FAST(value >= 0); // No spaces in enums: RBXASSERT_VERY_FAST(std::string(name).find(' ') == std::string::npos); // No no CamelCase in enums: RBXASSERT_VERY_FAST(!isCamel(name)); const Item* item = new Item(name, attributes, value, enumCount, *this); allItems.push_back(item); if (intToEnum.size()<=(size_t)value) intToEnum.resize(value+1, (Enum)-1); intToEnum[value] = value; RBXASSERT(value>=0); if (enumToIndex.size()<=(size_t)value) enumToIndex.resize(value+1, -1); enumToIndex[value] = enumCount; indexToEnum.push_back(value); if (enumToName.size()<=(size_t)value) enumToName.resize(value+1, &RBX::Name::getNullName()); enumToName[value] = &item->name; if (enumToString.size()<=(size_t)value) enumToString.resize(value+1); enumToString[value] = name; if (enumToItem.size()<=(size_t)value) enumToItem.resize(value+1); enumToItem[value] = item; nameToEnum[&item->name] = value; enumCount++; enumCountMSB = Math::computeMSB(enumCount); } void addLegacy(int oldValue, const char* name, Enum value) { RBXASSERT_VERY_FAST(value >= 0); if (intToEnum.size()<=(size_t)oldValue) intToEnum.resize(oldValue+1, (Enum)-1); intToEnum[oldValue] = value; nameToEnumLegacy[&RBX::Name::declare(name)] = value; } void addLegacyName(const char* name, Enum value) { nameToEnumLegacy[&RBX::Name::declare(name)] = value; } public: const RBX::Name& convertToName(const Enum& value) const { RBXASSERT(value>=0); RBXASSERT(value=enumToName.size()) return RBX::Name::getNullName(); return *enumToName[value]; } std::string convertToString(const Enum& value) const { RBXASSERT(value>=0); RBXASSERT((size_t)value=enumToString.size()) return ""; return enumToString[value]; } const Item* convertToItem(const Enum& value) const { RBXASSERT(value>=0); RBXASSERT((size_t)value=enumToItem.size()) return NULL; return enumToItem[value]; } bool mapIntValue(int intValue, Enum& value) const { if (intValue < 0) return false; if ((size_t)intValue >= intToEnum.size()) return false; value = intToEnum[intValue]; if ((int)value == -1) return false; return true; } bool convertToValue(const RBX::Name& name, Enum& value) const { typename std::map::const_iterator iter = nameToEnum.find(&name); if (iter!=nameToEnum.end()) { value = iter->second; return true; } iter = nameToEnumLegacy.find(&name); if (iter!=nameToEnumLegacy.end()) { value = iter->second; return true; } return false; } /*implement*/ const Item* lookup(const char* text) const { Enum e; if (convertToValue(RBX::Name::lookup(text), e)) return convertToItem(e); else return NULL; } /*implement*/ const Item* lookup(const Variant& value) const { return convertToItem(value.cast()); } /*implement*/ bool convertToValue(size_t index, Variant& value) const { Enum enumValue; bool result = convertToValue(index, enumValue); value = enumValue; return result; } /*implement*/ bool convertToString(size_t index, std::string& stringValue) const { Enum enumValue; if(convertToValue(index, enumValue)){ stringValue = convertToString(enumValue); return true; } return false; } bool convertToValue(const char* text, Enum& value) const { return convertToValue(RBX::Name::lookup(text), value); } size_t convertToIndex(Enum value) const { RBXASSERT(value>=0); if ((size_t)value \ const Type& Type::getSingleton() \ { \ return EnumDesc::singleton(); \ } \ template<> EnumRegistrar EnumRegistrar::registrar(0); \ template<> TypeRegistrar TypeRegistrar::registrar(0); \ }} // This class is intended to prevent clients of the library // from forgetting to initialize the enum descriptor template class EnumRegistrar : boost::noncopyable { int x; //// GCC does not generate the registrar variable defination & fails at Link Time. Force Construct by passing in an dummy arg to ctor. That works. WEIRD huh? EnumRegistrar(int i):x(i) { // This call registers the enum descriptor // in the reflection database EnumDesc::singleton(); } public: void dummy() { x++; } // The instantiation of this static member must be in a unit // that is initialized in the main thread before any objects // are created. Otherwise the reflection database // can change at runtime, which would be a disaster static EnumRegistrar registrar; }; } }