Merge pull request #19583 from hyarion/refactor/base-type

Refactor Base::Type
This commit is contained in:
Chris Hennes
2025-03-17 00:03:22 -05:00
committed by GitHub
23 changed files with 174 additions and 159 deletions

View File

@@ -32,7 +32,7 @@
using namespace Base;
Type BaseClass::classTypeId = Base::Type::badType(); // NOLINT
Type BaseClass::classTypeId = Base::Type::BadType;
//**************************************************************************
@@ -56,15 +56,14 @@ BaseClass::~BaseClass() = default;
void BaseClass::init()
{
assert(BaseClass::classTypeId == Type::badType() && "don't init() twice!");
assert(BaseClass::classTypeId.isBad() && "don't init() twice!");
/* Make sure superclass gets initialized before subclass. */
/*assert(strcmp(#_parentclass_), "inherited"));*/
/*Type parentType(Type::fromName(#_parentclass_));*/
/*assert(parentType != Type::badType() && "you forgot init() on parentclass!");*/
/*assert(!parentType.isBad() && "you forgot init() on parentclass!");*/
/* Set up entry in the type system. */
BaseClass::classTypeId =
Type::createType(Type::badType(), "Base::BaseClass", BaseClass::create);
BaseClass::classTypeId = Type::createType(Type::BadType, "Base::BaseClass", BaseClass::create);
}
Type BaseClass::getClassTypeId()
@@ -84,11 +83,11 @@ void BaseClass::initSubclass(Base::Type& toInit,
Type::instantiationMethod method)
{
// don't init twice!
assert(toInit == Base::Type::badType());
assert(toInit.isBad());
// get the parent class
Base::Type parentType(Base::Type::fromName(ParentName));
// forgot init parent!
assert(parentType != Base::Type::badType());
assert(!parentType.isBad());
// create the new type
toInit = Base::Type::createType(parentType, ClassName, method);

View File

@@ -65,7 +65,7 @@ private:
{ \
return _class_::classTypeId; \
} \
Base::Type _class_::classTypeId = Base::Type::badType(); \
Base::Type _class_::classTypeId = Base::Type::BadType; \
void* _class_::create(void) \
{ \
return new _class_(); \
@@ -99,7 +99,7 @@ private:
{ \
return _class_::classTypeId; \
} \
Base::Type _class_::classTypeId = Base::Type::badType(); \
Base::Type _class_::classTypeId = Base::Type::BadType; \
void* _class_::create(void) \
{ \
return 0; \

View File

@@ -44,7 +44,7 @@ PyObject* BaseClassPy::isDerivedFrom(PyObject* args)
}
Base::Type type = Base::Type::fromName(name);
bool valid = (type != Base::Type::badType() && getBaseClassPtr()->isDerivedFrom(type));
bool valid = (!type.isBad() && getBaseClassPtr()->isDerivedFrom(type));
return PyBool_FromLong(valid ? 1 : 0);
}

View File

@@ -33,61 +33,84 @@
using namespace Base;
static_assert(sizeof(Base::Type) == sizeof(Type::TypeId),
"Base::Type has been designed to be small to be passed around by value efficiently. "
"The size of Base::Type has changed. Be careful when adding more data members.");
static_assert(
sizeof(Base::Type) <= 2 * sizeof(void*),
"Base::Type has been designed to be small to be passed around by value efficiently. "
"When the size grows larger than ~2 words, consider passing by const reference instead. "
"Exact limit depends on the architecture and ABI.");
struct Base::TypeData
{
TypeData(const char* theName,
const Type type = Type::badType(),
const Type theParent = Type::badType(),
Type::instantiationMethod method = nullptr)
: name(theName)
, parent(theParent)
TypeData(const char* name,
const Type type,
const Type parent,
const Type::instantiationMethod instMethod)
: name(name)
, parent(parent)
, type(type)
, instMethod(method)
, instMethod(instMethod)
{}
std::string name;
Type parent;
Type type;
Type::instantiationMethod instMethod;
const std::string name;
const Type parent;
const Type type;
const Type::instantiationMethod instMethod;
};
namespace
{
constexpr const char* BadTypeName = "BadType";
}
std::map<std::string, unsigned int> Type::typemap;
std::vector<TypeData*> Type::typedata;
std::set<std::string> Type::loadModuleSet;
const Type Type::BadType;
Type::instantiationMethod Type::getInstantiationMethod() const
{
assert(typedata.size() >= 1 && "Type::init() must be called before creating instances");
assert(typedata.size() > index && "Type index out of bounds");
if (isBad() || typedata.size() <= index) {
return nullptr;
}
return typedata[index]->instMethod;
}
void* Type::createInstance() const
{
instantiationMethod method = typedata[index]->instMethod;
const auto method = getInstantiationMethod();
return method ? (*method)() : nullptr;
}
bool Type::canInstantiate() const
{
instantiationMethod method = typedata[index]->instMethod;
const auto method = getInstantiationMethod();
return method != nullptr;
}
void* Type::createInstanceByName(const char* TypeName, bool bLoadModule)
void* Type::createInstanceByName(const char* typeName, bool loadModule)
{
// if not already, load the module
if (bLoadModule) {
importModule(TypeName);
if (loadModule) {
importModule(typeName);
}
// now the type should be in the type map
Type type = fromName(TypeName);
if (type == badType()) {
return nullptr;
}
const Type type = fromName(typeName);
// let createInstance handle isBad check
return type.createInstance();
}
void Type::importModule(const char* TypeName)
void Type::importModule(const char* typeName)
{
// cut out the module name
const std::string mod = getModuleName(TypeName);
const std::string mod = getModuleName(typeName);
// ignore base modules
if (mod == "App" || mod == "Gui" || mod == "Base") {
@@ -95,45 +118,38 @@ void Type::importModule(const char* TypeName)
}
// remember already loaded modules
const auto pos = loadModuleSet.find(mod);
if (pos != loadModuleSet.end()) {
if (loadModuleSet.contains(mod)) {
return;
}
// lets load the module
Interpreter().loadModule(mod.c_str());
#ifdef FC_LOGLOADMODULE
Console().Log("Act: Module %s loaded through class %s \n", Mod.c_str(), TypeName);
Console().Log("Act: Module %s loaded through class %s \n", Mod.c_str(), typeName);
#endif
loadModuleSet.insert(mod);
}
std::string Type::getModuleName(const char* ClassName)
const std::string Type::getModuleName(const char* className)
{
std::string_view classNameView(ClassName);
std::string_view classNameView(className);
auto pos = classNameView.find("::");
return pos != std::string_view::npos ? std::string(classNameView.substr(0, pos))
: std::string();
}
Type Type::badType()
{
Type bad;
bad.index = 0;
return bad;
}
Type Type::createType(const Type& parent, const char* name, instantiationMethod method)
const Type Type::createType(const Type parent, const char* name, instantiationMethod method)
{
assert(name && name[0] != '\0' && "Type name must not be empty");
Type newType;
newType.index = static_cast<unsigned int>(Type::typedata.size());
TypeData* typeData = new TypeData(name, newType, parent, method);
Type::typedata.push_back(typeData);
Type::typedata.emplace_back(new TypeData(name, newType, parent, method));
// add to dictionary for fast lookup
Type::typemap[name] = newType.getKey();
Type::typemap.emplace(name, newType.getKey());
return newType;
}
@@ -141,11 +157,9 @@ Type Type::createType(const Type& parent, const char* name, instantiationMethod
void Type::init()
{
assert(Type::typedata.empty());
Type::typedata.push_back(new TypeData("BadType"));
Type::typemap["BadType"] = 0;
assert(Type::typedata.size() == 0 && "Type::init() should only be called once");
typedata.emplace_back(new TypeData(BadTypeName, BadType, BadType, nullptr));
typemap[BadTypeName] = 0;
}
void Type::destruct()
@@ -158,58 +172,59 @@ void Type::destruct()
loadModuleSet.clear();
}
Type Type::fromName(const char* name)
const Type Type::fromName(const char* name)
{
std::map<std::string, unsigned int>::const_iterator pos;
pos = typemap.find(name);
if (pos != typemap.end()) {
return typedata[pos->second]->type;
const auto pos = typemap.find(name);
if (pos == typemap.end()) {
return Type::BadType;
}
return Type::badType();
return typedata[pos->second]->type;
}
Type Type::fromKey(unsigned int key)
const Type Type::fromKey(TypeId key)
{
if (key < typedata.size()) {
return typedata[key]->type;
}
return Type::badType();
return BadType;
}
const char* Type::getName() const
{
assert(typedata.size() >= 1
&& "Type::init() must be called before fetching names, even for bad types");
return typedata[index]->name.c_str();
}
Type Type::getParent() const
const Type Type::getParent() const
{
assert(typedata.size() >= 1
&& "Type::init() must be called before fetching parents, even for bad types");
return typedata[index]->parent;
}
bool Type::isDerivedFrom(const Type& type) const
bool Type::isDerivedFrom(const Type type) const
{
Type temp(*this);
do {
if (temp == type) {
return true;
}
temp = temp.getParent();
} while (temp != badType());
} while (!temp.isBad());
return false;
}
int Type::getAllDerivedFrom(const Type& type, std::vector<Type>& List)
int Type::getAllDerivedFrom(const Type type, std::vector<Type>& list)
{
int cnt = 0;
for (auto it : typedata) {
if (it->type.isDerivedFrom(type)) {
List.push_back(it->type);
list.push_back(it->type);
cnt++;
}
}
@@ -221,17 +236,15 @@ int Type::getNumTypes()
return static_cast<int>(typedata.size());
}
Type Type::getTypeIfDerivedFrom(const char* name, const Type& parent, bool bLoadModule)
const Type Type::getTypeIfDerivedFrom(const char* name, const Type parent, bool loadModule)
{
if (bLoadModule) {
if (loadModule) {
importModule(name);
}
Type type = fromName(name);
if (type.isDerivedFrom(parent)) {
if (const Type type(fromName(name)); type.isDerivedFrom(parent)) {
return type;
}
return Type::badType();
return BadType;
}

View File

@@ -77,9 +77,10 @@ struct TypeData;
information: super classes must be registered before any of their
derived classes are.
*/
class BaseExport Type
class BaseExport Type final
{
public:
using TypeId = unsigned int;
/// Construction
Type(const Type& type) = default;
Type(Type&& type) = default;
@@ -87,34 +88,39 @@ public:
/// Destruction
~Type() = default;
/// creates a instance of this type
void* createInstance() const;
/// Creates an instance of this type
[[nodiscard]] void* createInstance() const;
/// Checks whether this type can instantiate
bool canInstantiate() const;
/// creates a instance of the named type
static void* createInstanceByName(const char* TypeName, bool bLoadModule = false);
static void importModule(const char* TypeName);
[[nodiscard]] bool canInstantiate() const;
/// Creates an instance of the named type
[[nodiscard]] static void* createInstanceByName(const char* typeName, bool loadModule = false);
using instantiationMethod = void* (*)();
static Type fromName(const char* name);
static Type fromKey(unsigned int key);
const char* getName() const;
Type getParent() const;
bool isDerivedFrom(const Type& type) const;
static int getAllDerivedFrom(const Type& type, std::vector<Type>& List);
/// Returns a type object by name
[[nodiscard]] static const Type fromName(const char* name);
/// Returns a type object by key
[[nodiscard]] static const Type fromKey(TypeId key);
/// Returns the name of the type
[[nodiscard]] const char* getName() const;
/// Returns the parent type
[[nodiscard]] const Type getParent() const;
/// Checks whether this type is derived from "type"
[[nodiscard]] bool isDerivedFrom(const Type type) const;
/// Returns all descendants from the given type
static int getAllDerivedFrom(const Type type, std::vector<Type>& list);
/// Returns the given named type if is derived from parent type, otherwise return bad type
static Type
getTypeIfDerivedFrom(const char* name, const Type& parent, bool bLoadModule = false);
static int getNumTypes();
static Type
createType(const Type& parent, const char* name, instantiationMethod method = nullptr);
unsigned int getKey() const;
bool isBad() const;
[[nodiscard]] static const Type
getTypeIfDerivedFrom(const char* name, const Type parent, bool loadModule = false);
/// Returns the number of types created so far
[[nodiscard]] static int getNumTypes();
/// Creates a new type with the given name, parent and instantiation method
[[nodiscard]] static const Type
createType(const Type parent, const char* name, instantiationMethod method = nullptr);
/// Returns the inner index of the type
[[nodiscard]] TypeId getKey() const;
/// Checks if the type is invalid
[[nodiscard]] bool isBad() const;
Type& operator=(const Type& type) = default;
Type& operator=(Type&& type) = default;
@@ -126,23 +132,28 @@ public:
bool operator>=(const Type& type) const;
bool operator>(const Type& type) const;
static Type badType();
static const Type BadType;
static void init();
static void destruct();
static std::string getModuleName(const char* ClassName);
/// Returns the name of the module the class is defined in
static const std::string getModuleName(const char* className);
private:
unsigned int index {0};
[[nodiscard]] instantiationMethod getInstantiationMethod() const;
static void importModule(const char* TypeName);
static std::map<std::string, unsigned int> typemap;
static std::vector<TypeData*> typedata;
TypeId index {BadTypeIndex};
static std::map<std::string, TypeId> typemap;
static std::vector<TypeData*> typedata; // use pointer to hide implementation details
static std::set<std::string> loadModuleSet;
static constexpr TypeId BadTypeIndex = 0;
};
inline unsigned int Type::getKey() const
inline Type::TypeId Type::getKey() const
{
return this->index;
}
@@ -179,7 +190,7 @@ inline bool Type::operator>(const Type& type) const
inline bool Type::isBad() const
{
return (this->index == 0);
return this->index == BadTypeIndex;
}
} // namespace Base

View File

@@ -76,8 +76,7 @@ PyObject* TypePy::getBadType(PyObject* args)
return nullptr;
}
Base::Type type = Base::Type::badType();
return new TypePy(new Base::Type(type));
return new TypePy(new Base::Type(Base::Type::BadType));
}
PyObject* TypePy::getParent(PyObject* args)
@@ -122,7 +121,7 @@ PyObject* TypePy::isDerivedFrom(PyObject* args)
return nullptr;
} while (false);
bool val = (type != Base::Type::badType() && getBaseTypePtr()->isDerivedFrom(type));
bool val = (!type.isBad() && getBaseTypePtr()->isDerivedFrom(type));
return PyBool_FromLong(val ? 1 : 0);
}