Files
2025-10-28 14:05:46 -04:00

376 lines
10 KiB
C++

#pragma once
#include "reflection/Type.h"
#include "util/utilities.h"
#include "util/math.h"
#include <boost/bind.hpp>
#include <boost/unordered_map.hpp>
#include <boost/thread/once.hpp>
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<enumCount)
Item(const char* name, Descriptor::Attributes attributes, int value, size_t index, const EnumDescriptor& owner)
:Descriptor(name, attributes)
,value(value)
,index(index)
,owner(owner)
{
}
bool convertToValue(Variant& value) const
{
return owner.convertToValue(index, value);
}
bool convertToString(std::string& value) const
{
return owner.convertToString(index, value);
}
};
#if 0
typedef boost::unordered_map<const RBX::Name*, const EnumDescriptor*> EnumNameTable;
#else
typedef std::map<const RBX::Name*, const EnumDescriptor*> EnumNameTable;
#endif
private:
static EnumNameTable& allEnumsNameLookup();
static std::vector<const EnumDescriptor*>& 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<const EnumDescriptor*>(&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<typename T>
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<typename Enum> class EnumRegistrar;
template<typename Enum>
class EnumDesc : public EnumDescriptor
{
public:
friend class Singleton<const EnumDesc<Enum> >;
static const EnumDesc& singleton()
{
return Singleton<const EnumDesc<Enum> >::singleton();
}
private:
// You must implement the following constructor for each EnumDesc that you define
EnumDesc();
~EnumDesc()
{
// Force linking of EnumRegistrar<Enum>, which will force clients
// of this library to define EnumRegistrar<Enum>::registrar in
// their startup code.
Reflection::EnumRegistrar<Enum>::registrar.dummy();
std::for_each(allItems.begin(), allItems.end(), &del_fun<const Item>);
}
std::map<const RBX::Name*, Enum> nameToEnum;
std::map<const RBX::Name*, Enum> 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<enumToItem.size());
if (value<0)
return RBX::Name::getNullName();
if ((size_t)value>=enumToName.size())
return RBX::Name::getNullName();
return *enumToName[value];
}
std::string convertToString(const Enum& value) const
{
RBXASSERT(value>=0);
RBXASSERT((size_t)value<enumToItem.size());
if (value<0)
return "";
if ((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());
if (value<0)
return NULL;
if ((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 RBX::Name*, Enum>::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<Enum>());
}
/*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<enumToIndex.size())
return enumToIndex[value];
else
return -1;
}
bool convertToValue(size_t index, Enum& value) const
{
if (index<enumCount) {
value = indexToEnum[index];
return true;
}
else {
return false;
}
}
};
// Helper macro
// 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?
#define RBX_REGISTER_ENUM(Enum) namespace RBX { namespace Reflection { \
template<> \
const Type& Type::getSingleton<Enum>() \
{ \
return EnumDesc<Enum>::singleton(); \
} \
template<> EnumRegistrar<Enum> EnumRegistrar<Enum>::registrar(0); \
template<> TypeRegistrar<Enum> TypeRegistrar<Enum>::registrar(0); \
}}
// This class is intended to prevent clients of the library
// from forgetting to initialize the enum descriptor
template<typename Enum>
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<Enum>::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;
};
}
}