Более или менее, у меня где-то работает так:
#include <iostream>
#include <functional>
#include <vector>
struct ClassEntry {
size_t id = 0;
const char* label;
};
class BaseClass {
public:
protected:
static void RegisterType(size_t id, const char * label) {
ClassEntry entry;
entry.id = id;
entry.label = label;
mRegisteredTypes.emplace_back(entry);
std::cout << "Registered type " << id << " label " << label << std::endl;
}
static size_t createId() {
static size_t id = 0;
return id++;
}
static std::vector<ClassEntry> mRegisteredTypes;
};
std::vector<ClassEntry> BaseClass::mRegisteredTypes;
class OneTimeCall {
public:
OneTimeCall(std::function<void(void)>&& func) {
func();
}
virtual ~OneTimeCall() {
}
};
template<typename T>
class MyClass : public BaseClass {
public:
MyClass() {
static OneTimeCall one_time {
[this]{
BaseClass::RegisterType(GetId(), T::GetType());
}
};
}
private:
protected:
static size_t GetId() {
static size_t id = BaseClass::createId();
return id;
}
};
class A : public MyClass<A> {
public:
A() {
}
static const char *GetType() {
return "ClassA";
}
};
class B : public MyClass<B> {
public:
B() {
}
static const char *GetType() {
return "ClassB";
}
};
int main() {
A a;
B b;
A a2;
B b2;
return 0;
}
Вывод:
Registered type 0 label ClassA
Registered type 1 label ClassB
Основная идея заключается в использовании CRTP и статической инициализации в конструкции для регистракаждый тип только один раз.В Linux это работает без проблем, в компиляторе Windows статический идентификатор BaseClass является новым для каждой библиотеки DLL, поэтому вам нужно немного настроить для использования во внешней библиотеке.
При таком подходе вам не нужна какая-либо внешняя библиотека, и ее можно компилировать без rtti.
Для наследования вы можете создать новый класс:
template<typename Current, typename Base>
class Mix : public MyClass<Current>, public Base {};
Таким образом, если вы передадите «тип C» в качестве текущего типа (CRTP), а тип A в качестве базового класса, может работать.
class C : public Mix<C, A> {
public:
C() {
}
static const char *GetType() {
return "ClassC";
}
};
При таком подходе, если вы ранее зарегистрировали «A», он не будет зарегистрирован снова, а если у вас нет «A», он будет зарегистрирован после «C».