// 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_FUNC(BeginPlay) VMEXPORTED_NATIVES_FUNC(Activate) VMEXPORTED_NATIVES_FUNC(Deactivate) VMEXPORTED_NATIVES_FUNC(DoSpecialDamage) VMEXPORTED_NATIVES_FUNC(UseInventory) 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; } #define VINDEX(cls, funcname) \ static int VIndex = -1; \ if (VIndex < 0) { \ VIndex = GetVirtualIndex(RUNTIME_CLASS(cls), #funcname); \ if (VIndex < 0) I_Error("Unable to find virtual function %s in " #cls, #funcname); \ } #define VFUNC this->GetClass()->Virtuals[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 { VINDEX(DObject, Destroy); // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; VMFrameStack stack; stack.Call(VFUNC, params, 1, nullptr, 0, nullptr); } } void Tick() { if (this->ObjectFlags & OF_SuperCall) { this->ObjectFlags &= ~OF_SuperCall; ExportedNatives::Get()->template Tick(this); } else { VINDEX(DThinker, Tick); // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; VMFrameStack stack; stack.Call(VFUNC, params, 1, nullptr, 0, nullptr); } } void PostBeginPlay() { if (this->ObjectFlags & OF_SuperCall) { this->ObjectFlags &= ~OF_SuperCall; ExportedNatives::Get()->template PostBeginPlay(this); } else { VINDEX(DThinker, PostBeginPlay); // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; VMFrameStack stack; stack.Call(VFUNC, params, 1, nullptr, 0, nullptr); } } void BeginPlay() { if (this->ObjectFlags & OF_SuperCall) { this->ObjectFlags &= ~OF_SuperCall; ExportedNatives::Get()->template BeginPlay(this); } else { VINDEX(AActor, BeginPlay); // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; VMFrameStack stack; stack.Call(VFUNC, params, 1, nullptr, 0, nullptr); } } void Activate(AActor *activator) { if (this->ObjectFlags & OF_SuperCall) { this->ObjectFlags &= ~OF_SuperCall; ExportedNatives::Get()->template Activate(this, activator); } else { VINDEX(AActor, Activate); // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)this, (DObject*)activator }; VMFrameStack stack; stack.Call(VFUNC, params, 2, nullptr, 0, nullptr); } } void Deactivate(AActor *activator) { if (this->ObjectFlags & OF_SuperCall) { this->ObjectFlags &= ~OF_SuperCall; ExportedNatives::Get()->template Deactivate(this, activator); } else { VINDEX(AActor, Deactivate); // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)this, (DObject*)activator }; VMFrameStack stack; stack.Call(VFUNC, params, 2, nullptr, 0, nullptr); } } int DoSpecialDamage(AActor *target, int damage, FName damagetype) { if (this->ObjectFlags & OF_SuperCall) { this->ObjectFlags &= ~OF_SuperCall; return ExportedNatives::Get()->template DoSpecialDamage(this, target, damage, damagetype); } else { VINDEX(AActor, DoSpecialDamage); // Without the type cast this picks the 'void *' assignment... VMValue params[4] = { (DObject*)this, (DObject*)target, damage, damagetype.GetIndex() }; VMReturn ret; VMFrameStack stack; int retval; ret.IntAt(&retval); stack.Call(VFUNC, params, 4, &ret, 1, nullptr); return retval; } } bool UseInventory(AInventory *item) { if (this->ObjectFlags & OF_SuperCall) { this->ObjectFlags &= ~OF_SuperCall; return ExportedNatives::Get()->template UseInventory(this, item); } else { VINDEX(AActor, UseInventory); // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)this, (DObject*)item }; VMReturn ret; VMFrameStack stack; int retval; ret.IntAt(&retval); stack.Call(VFUNC, params, 2, &ret, 1, nullptr); return !!retval; } } }; 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_FUNC(BeginPlay) VMEXPORT_NATIVES_FUNC(Activate) VMEXPORT_NATIVES_FUNC(Deactivate) VMEXPORT_NATIVES_FUNC(DoSpecialDamage) VMEXPORT_NATIVES_FUNC(UseInventory) VMEXPORT_NATIVES_END(AActor) /* VMEXPORT_NATIVES_START(AActor, DThinker) VMEXPORT_NATIVES_END(AActor) */