#include #include // We capture the base objects // lifetime style // You can add more here, // e.g. "Unique" for a unique pointer kind of lifestyle, // but you would need to think about how to handle // copying / moving in that case enum class BaseObjectLifetime { Value, Pointer, Shared }; // The base object // that we do inheritance and other work // on class BaseObject { public: BaseObject() { objectType = 0; } unsigned int getObjectType() const { return objectType; } bool doArmorThing() const { return false; } bool doWeaponThing() const { return false; } // helper function defined later after we define all the // base classes we care about sol::object getAsRetyped(lua_State* L, BaseObjectLifetime Lifetime) const; // For convenience with the customization points below int pushAsRetyped(lua_State* L, BaseObjectLifetime Lifetime) const { return getAsRetyped(L, Lifetime).push(L); } protected: unsigned int objectType; }; class Armor : public BaseObject { public: Armor() { objectType = 1; } bool doArmorThing() const { return true; } }; class Weapon : public BaseObject { public: Weapon() { objectType = 2; } bool doWeaponThing() const { return true; } }; // Get the most-derived type // that we care about from the base object, // obeying the lifetime type sol::object BaseObject::getAsRetyped(lua_State* L, BaseObjectLifetime Lifetime) const { switch (objectType) { case 1: std::cout << "Retyping as armor." << std::endl; switch (Lifetime) { case BaseObjectLifetime::Value: return sol::make_object(L, *static_cast(this)); case BaseObjectLifetime::Pointer: return sol::make_object(L, static_cast(this)); case BaseObjectLifetime::Shared: return sol::make_object(L, std::make_shared(*static_cast(this))); } case 2: std::cout << "Retyping as weapon." << std::endl; switch (Lifetime) { case BaseObjectLifetime::Value: return sol::make_object(L, *static_cast(this)); case BaseObjectLifetime::Pointer: return sol::make_object(L, static_cast(this)); case BaseObjectLifetime::Shared: return sol::make_object(L, std::make_shared(*static_cast(this))); } default: std::cout << "Unknown type: falling back to base object." << std::endl; // we have a normal type here, so that means we // must bypass typical customization points by using // sol::make_object_userdata/sol::make_reference_userdata // WARNING: IF THIS TYPE IS IN FACT NOT A BASE OBJECT, // BUT SOME DERIVED OBJECT, THEN RUNNING THIS CODE FOR // VALUE TYPES AND SHARED TYPES WILL "SLICE" // THE DERIVED BITS OFF THE BASE BITS PERMANENTLY // NEVER FORGET TO UPDATE THE SWITCH IF YOU ADD // NEW TYPES!! switch (Lifetime) { case BaseObjectLifetime::Value: return sol::make_object_userdata(L, *this); case BaseObjectLifetime::Shared: return sol::make_object_userdata(L, std::make_shared(*this)); case BaseObjectLifetime::Pointer: default: return sol::make_object_userdata(L, this); } } } // // sol customization points // // Defining a customization point that lets us put the correct object type on the stack. int sol_lua_push(sol::types, lua_State* L, const BaseObject& obj) { return obj.pushAsRetyped(L, BaseObjectLifetime::Value); } int sol_lua_push(sol::types, lua_State* L, const BaseObject* obj) { return obj->pushAsRetyped(L, BaseObjectLifetime::Pointer); } int sol_lua_push(sol::types>, lua_State* L, const std::shared_ptr& obj) { return obj->pushAsRetyped(L, BaseObjectLifetime::Shared); } int main() { // test our customization points out std::cout << "=== Base object customization points ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::string, sol::lib::table); lua["objectCache"] = lua.create_table(); // Do basic type binding. auto luaBaseObject = lua.new_usertype("tes3baseObject"); luaBaseObject["objectType"] = sol::readonly_property(&BaseObject::getObjectType); luaBaseObject["doArmorThing"] = &BaseObject::doArmorThing; luaBaseObject["doWeaponThing"] = &BaseObject::doWeaponThing; auto luaArmorObject = lua.new_usertype("tes3armor"); luaArmorObject[sol::base_classes] = sol::bases(); luaArmorObject["doArmorThing"] = &Armor::doArmorThing; auto luaWeaponObject = lua.new_usertype("tes3weapon"); luaWeaponObject[sol::base_classes] = sol::bases(); luaWeaponObject["doWeaponThing"] = &Weapon::doWeaponThing; // Objects we'll play with. BaseObject base; Armor armor; Weapon weapon; // Push some objects to lua. std::cout << "Normal pointers..." << std::endl; lua["ptrBase"] = &base; lua["ptrArmor"] = &armor; lua["ptrWeapon"] = &weapon; std::cout << std::endl; // Same objects but as base objects to test mapping. std::cout << "Base-cast pointers..." << std::endl; lua["ptrBaseAsBase"] = static_cast(&base); lua["ptrArmorAsBase"] = static_cast(&armor); lua["ptrWeaponAsBase"] = static_cast(&weapon); std::cout << std::endl; std::cout << "Smart direct pointers..." << std::endl; lua["sharedBase"] = std::make_shared(); lua["sharedArmor"] = std::make_shared(); lua["sharedArmor"] = std::make_shared(); std::cout << std::endl; std::cout << "Smart pointers put as the base class..." << std::endl; lua["sharedBaseAsBase"] = (std::shared_ptr)std::make_shared(); lua["sharedArmorAsBase"] = (std::shared_ptr)std::make_shared(); lua["sharedArmorAsBase"] = (std::shared_ptr)std::make_shared(); std::cout << std::endl; return 0; }