#pragma once #include <typeinfo> #include <util/generic/hash.h> #include <util/generic/vector.h> #include <util/ysafeptr.h> //////////////////////////////////////////////////////////////////////////////////////////////////// // factory is using RTTI // objects should inherit T and T must have at least 1 virtual function template <class T> class TClassFactory { public: typedef const std::type_info* VFT; private: typedef T* (*newFunc)(); typedef THashMap<int, newFunc> CTypeNewHash; // typeID->newFunc() typedef THashMap<VFT, int> CTypeIndexHash; // vftable->typeID CTypeIndexHash typeIndex; CTypeNewHash typeInfo; void RegisterTypeBase(int nTypeID, newFunc func, VFT vft); static VFT GetObjectType(T* pObject) { return &typeid(*pObject); } int VFT2TypeID(VFT t) { CTypeIndexHash::iterator i = typeIndex.find(t); if (i != typeIndex.end()) return i->second; for (i = typeIndex.begin(); i != typeIndex.end(); ++i) { if (*i->first == *t) { typeIndex[t] = i->second; return i->second; } } return -1; } public: template <class TT> void RegisterType(int nTypeID, newFunc func, TT*) { RegisterTypeBase(nTypeID, func, &typeid(TT)); } void RegisterTypeSafe(int nTypeID, newFunc func) { TPtr<T> pObj = func(); VFT vft = GetObjectType(pObj); RegisterTypeBase(nTypeID, func, vft); } T* CreateObject(int nTypeID) { newFunc f = typeInfo[nTypeID]; if (f) return f(); return nullptr; } int GetObjectTypeID(T* pObject) { return VFT2TypeID(GetObjectType(pObject)); } template <class TT> int GetTypeID(TT* p = 0) { (void)p; return VFT2TypeID(&typeid(TT)); } void GetAllTypeIDs(TVector<int>& typeIds) const { typeIds.clear(); for (typename CTypeNewHash::const_iterator iter = typeInfo.begin(); iter != typeInfo.end(); ++iter) { typeIds.push_back(iter->first); } } }; //////////////////////////////////////////////////////////////////////////////////////////////////// template <class T> void TClassFactory<T>::RegisterTypeBase(int nTypeID, newFunc func, VFT vft) { if (typeInfo.find(nTypeID) != typeInfo.end()) { TObj<IObjectBase> o1 = typeInfo[nTypeID](); TObj<IObjectBase> o2 = func(); // stupid clang warning auto& o1v = *o1; auto& o2v = *o2; if (typeid(o1v) != typeid(o2v)) { fprintf(stderr, "IBinSaver: Type ID 0x%08X has been already used\n", nTypeID); abort(); } } CTypeIndexHash::iterator typeIndexIt = typeIndex.find(vft); if (typeIndexIt != typeIndex.end() && nTypeID != typeIndexIt->second) { fprintf(stderr, "IBinSaver: class (Type ID 0x%08X) has been already registered (Type ID 0x%08X)\n", nTypeID, typeIndexIt->second); abort(); } typeIndex[vft] = nTypeID; typeInfo[nTypeID] = func; } //////////////////////////////////////////////////////////////////////////////////////////////////// // macro for registering CFundament derivatives #define REGISTER_CLASS(factory, N, name) factory.RegisterType(N, name::New##name, (name*)0); #define REGISTER_TEMPL_CLASS(factory, N, name, className) factory.RegisterType(N, name::New##className, (name*)0); #define REGISTER_CLASS_NM(factory, N, name, nmspace) factory.RegisterType(N, nmspace::name::New##name, (nmspace::name*)0);