// Templates really are powerful #define VMEXPORTED_NATIVES_START \ template class ExportedNatives : public ExportedNatives {}; \ template<> class ExportedNatives { \ protected: ExportedNatives() {} \ public: \ static ExportedNatives *Get() { static ExportedNatives *Instance = nullptr; if (Instance == nullptr) Instance = new ExportedNatives; return Instance; } \ ExportedNatives(const ExportedNatives&) = delete; \ ExportedNatives(ExportedNatives&&) = delete; #define VMEXPORTED_NATIVES_FUNC(func) \ template ret func(void *ptr, args ... arglist) { return ret(); } #define VMEXPORTED_NATIVES_END }; #define VMEXPORT_NATIVES_START(cls, parent) \ template<> class ExportedNatives : public ExportedNatives { \ protected: ExportedNatives() {} \ public: \ static ExportedNatives *Get() { static ExportedNatives *Instance = nullptr; if (Instance == nullptr) Instance = new ExportedNatives; return Instance; } \ ExportedNatives(const ExportedNatives&) = delete; \ ExportedNatives(ExportedNatives&&) = delete; #define VMEXPORT_NATIVES_FUNC(func) \ template ret func(void *ptr, args ... arglist) { return static_cast(ptr)->object::func(arglist...); } #define VMEXPORT_NATIVES_END(cls) }; //Initial list VMEXPORTED_NATIVES_START VMEXPORTED_NATIVES_FUNC(Destroy) VMEXPORTED_NATIVES_FUNC(Tick) VMEXPORTED_NATIVES_FUNC(PostBeginPlay) VMEXPORTED_NATIVES_END inline int GetVirtualIndex(PClass *cls, const char *funcname) { // Look up the virtual function index in the defining class because this may have gotten overloaded in subclasses with something different than a virtual override. auto sym = dyn_cast(cls->Symbols.FindSymbol(funcname, false)); assert(sym != nullptr); auto VIndex = sym->Variants[0].Implementation->VirtualIndex; assert(VIndex >= 0); return VIndex; } template class DVMObject : public T { public: static char *FormatClassName() { static char *name = nullptr; if (name == nullptr) { name = new char[64]; mysnprintf(name, 64, "DVMObject<%s>", Super::RegistrationInfo.Name); atterm([]{ delete[] DVMObject::RegistrationInfo.Name; }); } return name; } virtual PClass *StaticType() const { return RegistrationInfo.MyClass; } static ClassReg RegistrationInfo; static ClassReg * const RegistrationInfoPtr; typedef T Super; private: typedef DVMObject ThisClass; static void InPlaceConstructor(void *mem) { new((EInPlace *)mem) DVMObject; } public: void Destroy() { if (this->ObjectFlags & OF_SuperCall) { this->ObjectFlags &= OF_SuperCall; ExportedNatives::Get()->template Destroy(this); } else { static int VIndex = -1; if (VIndex < 0) VIndex = GetVirtualIndex(RUNTIME_CLASS(DObject), "Destroy"); // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; VMFrameStack stack; stack.Call(this->GetClass()->Virtuals[VIndex], params, 1, nullptr, 0, nullptr); } } void Tick() { ExportedNatives::Get()->template Tick(this); } void PostBeginPlay() { ExportedNatives::Get()->template PostBeginPlay(this); } }; template ClassReg DVMObject::RegistrationInfo = { nullptr, DVMObject::FormatClassName(), &DVMObject::Super::RegistrationInfo, nullptr, nullptr, DVMObject::InPlaceConstructor, nullptr, sizeof(DVMObject), DVMObject::MetaClassNum }; template _DECLARE_TI(DVMObject) VMEXPORT_NATIVES_START(DObject, void) VMEXPORT_NATIVES_FUNC(Destroy) VMEXPORT_NATIVES_END(DObject) VMEXPORT_NATIVES_START(DThinker, DObject) VMEXPORT_NATIVES_FUNC(Tick) VMEXPORT_NATIVES_FUNC(PostBeginPlay) VMEXPORT_NATIVES_END(DThinker) /* VMEXPORT_NATIVES_START(AActor, DThinker) VMEXPORT_NATIVES_END(AActor) */