Not able to modify C++ object passed as parameter to Lua function - c++

I have been working on SDL2 2D Game Engine for several years now. Just ditched inheritance approach to define game entities with composition approach where I have Entity class and it has vector of Component classes and recently I got into lua, because I want to define entities using Lua table with optional callback functions.
Working parts
I am using Lua5.4 and C API to bind some engine methods and Entity class to Lua. I use XML file to load list of scripts for each Entity defined by Lua:
<script name="player" filename="scripts/player.lua" type="entity"/>
Then Entity gets created in C++ with ScriptComponent which holds a pointer to Lua state. Lua file gets loaded at this point and state is not closed unless Entity is destroyed.
player.lua script might look something like this:
-- Entity
player = {
-- Entity components
transform = {
X = 100,
Y = 250
},
physics = {
mass = 1.0,
friction = 0.2
},
sprite = {
id = "player",
animation = {},
width = 48,
height = 48
},
collider = {
type = "player",
onCollide = function(this, second)
print("Lua: onCollide() listener called!")
end
},
HP = 100
}
Using this I managed to create each Component class using Lua C API with no issues. Also while loading this I detect and set "onCollide" function in Lua.
Also I have managed to register some Engine functions so I can call them to lua:
playSound("jump") in C++:
static int lua_playSound(lua_State *L) {
std::string soundID = (std::string)lua_tostring(L, 1);
TheSoundManager::Instance()->playSound(soundID, 0);
return 0;
}
Also have created meta table for Entity class with __index and __gc metamethods and it works if I call these methods with Entity created in Lua outside of player table, such as:
-- This goes in player.lua script after the main table
testEntity = Entity.create() -- works fine, but entity is created in Lua
testEntity:move(400, 400)
testEntity:scale(2, 2)
testEntity:addSprite("slime", "assets/sprite/slime.png", 32, 32)
Problem
Now whenever collision happens and Entity has ScriptComponent, it correctly calls onCollide method in Lua. Even playSound method inside triggers correctly. Problem is when I try to manipulate Entities which are passed as this and seconds arguments to onCollide
onCollide = function(this, second)
print(type(this)) -- userdata
print(type(second)) --userdata
--Entity.scale(this, 10, 10) --segfault
--this:scale(10, 10) --segfault
playSound("jump") -- works fine, does not need any metatables
end
This is how I am calling onCollide method and passing existing C++ object to Lua:
// This is found in a method which belongs to ScriptComponent class, it holds lua state
// owner is Entity*, all Components have this
// second is also Entity*
if (lua_isfunction(state, -1)) {
void* self = (Entity*)lua_newuserdata(state, sizeof(Entity));
self = owner;
luaL_getmetatable(state, "EntityMetaTable");
assert(lua_isuserdata(state, -2));
assert(lua_istable(state, -1));
lua_setmetatable(state, -2);
assert(lua_isuserdata(state, -1));
void* second = (Entity*)lua_newuserdata(state, sizeof(Entity));
second = entity;
luaL_getmetatable(state, "EntityMetaTable");
lua_setmetatable(state, -2);
// Code always reaches cout statement below unless I try to manipulate Entity
// objects passed to Lua in Lua
if (luaOk(state, lua_pcall(state, 2, 0, 0))) {
std::cout << "onCollide() Called sucessfully!!!" << std::endl;
}
script->clean(); // Cleans lua stack
return;
}
So basically I have managed to load data from table, bind and use some methods from C++ engine and mapped Entity class using metatable and __index and __gc meta methods which work fine for objects created in Lua but not when I try to pass existing C++ object and set existing meta table.
I still think I will be alright without using any Lua binders, because all I wanted here is to load data for all Components which works fine and script some behaviour based on events which also almost works except for not being able to correctly pass existing C++ object to onCollide method. Thank you for your help!

It is impossible to "pass an existing C++ object to Lua". You would either have to copy it to the storage you get with lua_newuserdata, or use lua_newuserdata from the beginning.
Depending on what you can guarantee about the lifetime of the entity, you have several options:
If the lifetime of every entity aligns with the lifetime of the Lua instance, you can let Lua manage the entity object completely through its GC, i.e. call lua_newuserdata and use the "placement new" operator on the resulting object, initializing the entity. Note however that you have to prevent the object from getting freed by the Lua GC somehow if you only use it outside Lua, by adding it to the registry table for example.
If the lifetime of every entity exceeds the lifetime of the Lua instance, you can use *static_cast<Entity**>(lua_newuserdata(state, sizeof(Entity*))) = self;, i.e. allocate a pointer-sized userdata and store the entity pointer there. No need for anything complex here.
If you manage the entity through a smart pointer, you can allocate it in the Lua instance and take advantage of its GC. In this case, the code is a bit more complex however:
#include <type_traits>
#if __GNUG__ && __GNUC__ < 5
#define is_trivially_constructible(T) __has_trivial_constructor(T)
#define is_trivially_destructible(T) __has_trivial_destructor(T)
#else
#define is_trivially_constructible(T) std::is_trivially_constructible<T>::value
#define is_trivially_destructible(T) std::is_trivially_destructible<T>::value
#endif
namespace lua
{
template <class Type>
struct _udata
{
typedef Type &type;
static Type &to(lua_State *L, int idx)
{
return *reinterpret_cast<Type*>(lua_touserdata(L, idx));
}
static Type &check(lua_State *L, int idx, const char *tname)
{
return *reinterpret_cast<Type*>(luaL_checkudata(L, idx, tname));
}
};
template <class Type>
struct _udata<Type[]>
{
typedef Type *type;
static Type *to(lua_State *L, int idx)
{
return reinterpret_cast<Type*>(lua_touserdata(L, idx));
}
static Type *check(lua_State *L, int idx, const char *tname)
{
return reinterpret_cast<Type*>(luaL_checkudata(L, idx, tname));
}
};
template <class Type>
typename _udata<Type>::type touserdata(lua_State *L, int idx)
{
return _udata<Type>::to(L, idx);
}
template <class Type>
typename _udata<Type>::type checkudata(lua_State *L, int idx, const char *tname)
{
return _udata<Type>::check(L, idx, tname);
}
template <class Type>
Type &newuserdata(lua_State *L)
{
auto data = reinterpret_cast<Type*>(lua_newuserdata(L, sizeof(Type)));
if(!is_trivially_constructible(Type))
{
new (data) Type();
}
if(!is_trivially_destructible(Type))
{
lua_createtable(L, 0, 2);
lua_pushboolean(L, false);
lua_setfield(L, -2, "__metatable");
lua_pushcfunction(L, [](lua_State *L) {
lua::touserdata<Type>(L, -1).~Type();
return 0;
});
lua_setfield(L, -2, "__gc");
lua_setmetatable(L, -2);
}
return *data;
}
template <class Type>
void pushuserdata(lua_State *L, const Type &val)
{
newuserdata<Type>(L) = val;
}
template <class Type, class=typename std::enable_if<!std::is_lvalue_reference<Type>::value>::type>
void pushuserdata(lua_State *L, Type &&val)
{
newuserdata<typename std::remove_reference<Type>::type>(L) = std::move(val);
}
}
If self is std::shared_ptr<Entity> you can then do lua::pushuserdata(state, self); and it will take care of everything ‒ properly initialize the userdata, and add a __gc metamethod that frees it. You may also do lua::pushuserdata<std::weak_ptr<Entity>>(state, self); if you don't want to let Lua prolong the lifetime of the entities. In-place construction is also possible too, if you modify lua::newuserdata accordingly.

Related

Generic types with c++ and lua

I have an Entity class that contains some attributes (pieces of data). These attributes are stored in a map from name => value.
class Entity
{
public:
// Sets the attribute with the specified name.
void attribute(const std::string& name, const GenericType& value) {
m_attributes[name] = value;
}
// Returns the attribute with the specified name.
GenericType& attribute(const std::string& name) {
return m_attributes[name];
}
template<typename AttributeType>
void attribute(const std::string& name, const AttributeType& value) {
m_attributes[name] = GenericType(value);
}
template<typename AttributeType>
AttributeType& attribute(const std::string& name) {
return generic_cast<AttributeType>(m_attributes[name]);
}
private:
// Map of attributes from name => value.
std::unordered_map<std::string, GenericType> m_attributes;
}
I have a method to create or overwrite an attribute and another method that returns the attribute with the specified name. The first two methods are exposed to Lua. The last two methods are for accessing and modifying attributes from my c++ source code. The problem is that attributes can be of any type. For example, I want to be able to do:
entity.attribute("Health", 100)
entity.attribute("Position", Vector3(1,2,3))
entity.attribute("Position").x = 4
It should be possible to read and modify attributes both from my c++ source files and from lua scripts. I have previously been using ChaiScript where I used the Boxed_Value class as my GenericType. This worked well but excessive compile times have forced me to look elsewhere.
Is there a way to achieve this with LuaBind (or any other Lua binding library)? The luabind::object class looks promising but it takes a lua_State pointer in its constructor. This worries me as I feel that the Entity class really shouldn't know anything about the Lua state.
In LuaBind your entity class doesn't need to know anything about lua state. A Lua binding does not have to be 1-to-1 syntactically identical to the C++ API, as these are quite different languages.
In your case, I would rather split the api into getters and setters. With the shown Entity class you might get struggling with making LuaBind do what you want to do unambiguously. What you could do is write a wrapper class for Entity on the C++ side, which would have a LuaBind-conforming, simple interface, i.e. splitting getters and setters using unambiguous names.
Blufs is an example, demonstrating what I mean.
As a simple example, with LuaBridge:
somewhat tricky to create a simple, non hand-rolled binding:
class Entity {
std::map<std::string, int> attributes;
public:
void attribute(std::string const& key,int value) {
attributes[key] = value;
}
int& attribute(std::string const& key) {
return attributes[key];
}
};
a wrapper can be bound instead:
class EntityWrapper {
Entity entity;
public:
void set_int(std::string const& key,int value) {
entity.attribute(key,value);
}
int get_int(std::string const& key) {
return entity.attribute(key);
}
};
a simple binding:
void luabridge_bind(lua_State *L) {
luabridge::getGlobalNamespace(L)
.beginClass<EntityWrapper>("Entity")
.addConstructor<void(*)(), RefCountedPtr<EntityWrapper> /* creation policy */ >()
.addFunction("get_int", &EntityWrapper::get_int)
.addFunction("set_int", &EntityWrapper::set_int)
.endClass()
;
}
and in Lua:
local e = Entity()
e:set_int("bla",42)
print(e:get_int("bla"))
If you need Entity to interact with other APIs, write small wrappers that get the original wrapped object and pass it to other functions.

Binding C++ functions & calling Lua functions with class as parameter

I've been reading about Lua/C++ and I had a question about design I was hoping I could get some help on.
What I want:
I want so my class in C++ (Created and Destroyed by C++) to be able to call
a Lua function using itself as a parameter.
Eg.
object.h
class Object
{
public:
Object(const std::string & onLoad, const std::string & onEvent);
~Object();
void OnLoad();
void RegisterEvent(const std::string & eventID);
void OnEvent(const std::string & eventID);
void SetValue(int value);
private:
int m_value;
std::string m_onLoad;
std::string m_onEvent;
};
object.cpp
Object::Object(const std::string & onLoad, const std::string & onEvent)
: m_value(0)
, m_onLoad(onLoad)
, m_onEvent(onEvent)
{
}
Object::~Object()
{
GAME->GetEventManager()->UnregisterListener(this);
}
void Object::OnLoad()
{
//
// call lua function [name store in: m_onLoad]
// eg. m_onLoad = Object_OnLoad
// in lua ->
// function Object_OnLoad(object)
//
}
void Object::RegisterEvent(const std::string & eventID)
{
GAME->GetEventManager()->RegisterEvent(this, eventID);
}
void Object::OnEvent()
{
//
// call lua function [name store in: m_onEvent]
// eg. m_onEvent = Object_OnEvent
// in lua ->
// function Object_OnEvent(object, eventID)
//
}
void Object::SetValue(int value)
{
m_value = value;
}
script.lua
function Object_OnLoad(object)
object:RegisterEvent("EVENT_CURRENT_HEALTH_CHANGED")
end
function Object_OnEvent(object, eventID)
if (eventID == "EVENT_CURRENT_HEALTH_CHANGED")
object:SetValue(GetCurrentHealth());
end
test.cpp
Object *pTest = new Object("Object_OnLoad", "Object_OnEvent");
pTest->OnLoad();
GAME->GetEventManager()->TriggerEvent(CEvent("EVENT_CURRENT_HEALTH_CHANGED"));
delete pTest;
After Some reading:
From what I've read this is no direct way to assign C++ class instance functions.
Non-member functions are needed. Tables are used to track functions.
My Questions:
What do I push as an argument when calling the Lua functions (Object_OnEvent(object, eventID) etc...) Is it a pointer to the object
How does Lua know the object design
Do I need a table per object or instance
Do I need to duplicate all the functions I intend to use in Lua again as normal functions grabbing a the ptr to call it from
As a final and possible single question:
Is there any place I could get more information on what I'm trying to achieve described above.
I'm probably just going to go back to step one and try and absorb this information again.
I still wan't to make my post tho. I'll post back myself if I set it up.
There are many questions, but in principle, if I understand you correctly, you want to bind your C++ classes to Lua, have a shared object lifetime and automatic garbage collection, and be able to call Lua functions on objects created on the C++ side.
This is all possible with either low-level glue code, or dedicated binding libraries, such as LuaBridge and LuaState. LuaState is used in my answer for convenience and fast prototyping.
What's not yet clear is why you want to define a trivial function in Lua, such as Object_OnLoad to call it from C++, which would call a method of an object that you have created in the same scope on the C++ side. I'd guess, you have a more complicated picture in your code, so that such Lua usage pattern would be justified. In that case, one by one:
The ingredients
Binding a class to Lua
Here's a declarative binding that you can call once before calling any other Lua functions
void luabridge_bind(lua_State *L) {
luabridge::getGlobalNamespace(L)
.beginClass<MyObject>("MyObject")
.addConstructor<void(*)(), RefCountedPtr<MyObject> /* creation policy */ >()
.addFunction("RegisterEvent", &MyObject::RegisterEvent)
.endClass()
;
}
To perform the binding:
lua::State state;
luabridge_bind(state.getState());
Calling a lua function on a C++ side object
LuaState unfortunately cannot use objects in call parameters at the moment, while primitives work, i.e. from the readme:
state.doString("function add(x, y) return x + y end");
int result = state["add"](1,2);
But what one could do is to temporary create a global variable instance (watch out for name collisions) and call the function on it.
Preparing the script:
static const char *script =
"function Object_OnLoad(object)\n"
" object:RegisterEvent('EVENT_CURRENT_HEALTH_CHANGED')\n"
"end"
;
state.doString(script);
Creating an automatically lifetime-managed object:
auto my_obj = RefCountedPtr<MyObject>(new MyObject);
Calling the lua function on the object:
SetGlobal(state.getState(), "my_obj", my_obj);
state.doString("Object_OnLoad(my_obj); my_obj = nil");
Where SetGlobal can look like that:
template <typename T>
void SetGlobal(lua_State* L, const char *name, T value) {
luabridge::push(L, value);
lua_setglobal(L, name);
}
A complete example and comments
You can find the whole example code at Github: try_luabridge.cpp
which has been compiled and run at Travis CI.
The possibilities are limitless. It's up to you how you structure your code, so, naturally, this answer won't provide code that would immediately fit your needs. However, I'd encourage you to read Programming in Lua, and LuaBridge and LuaState manuals to get a better overview of the possiblities that are at your hand.

Storing a reference to a Lua value in C, how can it be done?

For example, let's say I have a keyhandling interface, defined in C++ as :
class KeyBoardHandler
{
public:
virtual onKeyPressed(const KeyEventArgs& e);
virtual onKeyReleased(const KeyEventArgs& e);
}
Now, I want to extend this to Lua, to allow Lua to take advantage and register a KeyboardHandler in script.
Here's the prototype so far.
class ScriptKeyboardHandler : public KeyboardHandler
{
public:
... previous methods omitted
static void createFromScript(lua_State* L);
bool createCppData();
private:
ScriptKeyBoardHandler(lua_State* L);
int mSelf;
int mKeyPressFunc;
int mKeyReleaseFunc;
lua_State* mpLuaState;
}
Now, I know the implementation will go something like this:
ScriptKeyboardHandler::ScriptKeyboardHandler(lua_State* L) :
mpState(L)
{ }
ScriptKeyboardHandler::onKeyPressed(...) {
// check if mKeyPressFunc is a function
// call it, passing in mself, and the event args as params
}
// On Key Release omitted because being similar to the key pressed
ScriptKeyboardHandler::createFromScript(lua_State* L)
{
auto scriptKeyboardHandler = new ScriptKeyboardHandler(L);
if (scriptKeyboardHandler->createCppData())
{
// set the light user data and return a reference to ourself (mSelf)
}
}
ScriptKeyboardHandler::createCppData()
{
// get the constructor data (second param) and find the keyPressed and keyReleased function, store those for later usage
// any other data within the constructor data will apply to object
}
-- Syntax of the lua code
MyScriptHandler = { }
MyScriptHandler.__index = MyScriptHandler
MyScriptHandler.onKeyPress = function(self, args)
end
handler = createScriptHandler(handler, MyScriptHandler)
registerKeyHandler(handler)
I just don't know how to find the functions when they are passed in as arguments within a table.
Am I doing this right? I hope I am, it's been a pain the ass because tolua doesn't support virtual classes easily, not ones you can derive from in script anyway.
I'm not worried about the other functions, just how do I find these variables (the key press function, etc, from my C code
Here's roughly what my implementation of onKeyPressed would look like.
void ScriptKeyboardHandler::onKeyPressed()
{
//Get the table corresponding to this object from the C registry
lua_pushlightuserdata(mpLuaState, this);
lua_gettable(mpLuaState,LUA_REGISTRYINDEX);
//Now get the function to call from the object
lua_pushstring(mpLuaState,"onKeyPress");
lua_gettable(mpLuaState,-2);
//Now call the function
lua_pushvalue(mpLuaState, -2 ); // Duplicate the self table as first argument
//TODO: Add the other arguments
lua_pcall(mpLuaState, 1, 0, 0 ); // TODO: You'll need some error checking here and change the 1 to represent the number of args.
lua_pop(mpLuaState,1); //Clean up the stack
}
However you'll also need to update the constructor to store the lua object representing the handler into the registry.

What's a good safe way to initialise memory for types I don't yet know about?

I started thinking about this after receiving an answer for this question. This is a bit tricky to explain, but I'll do my best.
I'm building a small(ish) 2D game engine. There are certain requirements that I need to satisfy, since this engine has to "work" with existing code that others have written for a different engine. Some change to existing code is inevitable, but I want to minimise it.
Users of my engine need to define entities called "gadgets". These are basically structs containing shapes and other state variables. These "gadgets" fall into classes, e.g. they may decide to define an icon gadget or a button gadget - or whatever.
They will also define a message handler for that class of gadgets.
E.g.
typedef struct
{
shape shapelist[5];
int num_options;
}interface;
static void interface_message_handler( interface * myself, message * msg )
{
switch( msg->type )
{
case NEW_MSG:
{
interface_descriptor * desc = msg->desc;
// initialize myself with contents of this message.
...
}
break;
....
}
}
Users have already given me the corresponding message handler function and also the number of bytes in a interface object. And they can then ask the engine to create new instances of their gadgets via IDs e.g:
engine->CreateNewGadget( interface_gadget_class_ID, welcome_interface_ID );
where interface_gadget_class_ID is the ID for that class of gadgets and welcome_interface_ID is the instance ID. At some point during CreateNewGadget I need to a) allocate memory to hold a new gadget and then call the gadget class's message handler on it, with a NEW_MSG so that it can initialize itself.
The problem is, if all I'm doing is allocating memory - that memory is uninitialized (and that means all the struct members are uninitialized - so if interface contains a vector, for example, then I'm going to get some wierd results if the message handler does anything with it ).
To avoid wierd results caused by doing stuff to unintialized memory, I really need to call a constructor for that memory as well before passing it to the gadget's message handler function.
e.g in the case of interface:
pfunc(new (memory) interface);
But my question is, if I have no knowledge of the types that users are creating, how can I do that?
// We create a typedef that refers to a function pointer
// which is a function that returns an interface pointer
typedef interface * (*GadgetFactory)(void);
// we'll actually create these functions by using this template function
// Different version of this function will produce different classes.
template<typename T>
interface * create_object()
{
return new T;
}
// This function takes care of setting everything up.
template<typename T>
void RegisterGadgetType(int gadget_type_id)
{
// Get outselves a copy of a pointer to the function that will make the object
GadgetFactory factory = create_object<T>;
// store factory somewhere
}
interface * CreateGadget(int gadget_type_id)
{
// get factory
GadgetFactory factory;
// factory will give me the actual object type I need.
return (*factory)();
}
RegisterGadgetType<S>(2);
CreateGadget(2);
as i see it, you always know because interface_gadget_class_ID defines the type to create.
you create a base c++ class: (corresponds to class interface in your example). this base class contains all of data members which are used by every interface subclass (that is, every gadget).
the base class also declares all methods common to every gadget. example: each gadget is able to receive a call handleMessage. handleMessage is pure virtual, because this method is the subclasses' role to fulfill.
then you extend/subclass to support the stuff you have to do with each gadget's specialization. at this point, you add the members and methods specific to each gadget subclass.
CreateNewGadget serves as a factory for all your subclasses, where the arguments determine which class you will create.
from there, c++ will handle construction/destruction, allocation sizes, etc..
if you're allowing plugins with their own factories in your engine, then you'll need another level, where third parties register their custom types and inherit from your base(s).
here's a simple layout of the interfaces (in non-compiled pseudo code):
namespace MONGadgets {
class t_interface {
protected:
t_interface(/* ... */);
public:
virtual ~t_interface();
/* each subclass must override handleMessage */
virtual t_result handleMessage(const t_message& message) = 0;
};
namespace InterfaceSubclasses {
class t_gadget1 : public t_interface {
public:
t_gadget1(const welcome_interface_ID& welcome);
virtual ~t_gadget1();
virtual t_result handleMessage(const t_message& message) {
std::cout << "t_gadget1\n";
}
/* gadget1 has no specific instance variables or methods to declare */
};
class t_gadget2 : public t_interface {
public:
t_gadget2(const welcome_interface_ID& welcome);
virtual ~t_gadget2();
virtual t_result handleMessage(const t_message& message) {
std::cout << "t_gadget2\n";
}
private:
/* here is an example of a method specific to gadget2: */
void drawShape(const unsigned& idx);
private:
/* here is gadget2's unique data: */
shape shapelist[5];
int num_options;
};
namespace ClassID {
enum { Gadget1 = 1, Gadget2 = 2 };
}
}
/* replaced by virtual t_result t_interface::handleMessage(const t_message&)
- static void interface_message_handler( interface * myself, message * msg );
*/
class t_gadget_factory {
public:
t_interface* CreateNewGadget(const interface_gadget_class_ID& classID, const welcome_interface_ID& welcome) {
switch (classID) {
case InterfaceSubclasses::ClassID::Gadget1 :
return new InterfaceSubclasses::gadget1(welcome);
case InterfaceSubclasses::ClassID::Gadget2 :
return new InterfaceSubclasses::gadget2(welcome);
/* ... */
}
}
};
}
Example code (ignoring my other suggestion, about factories and virtual functions):
typedef struct
{
shape shapelist[5];
int num_options;
} interface;
static void interface_message_handler( void * myself, message * msg )
{
switch( msg->type )
{
case NEW_MSG:
{
interface *self = new (myself) interface;
interface_descriptor * desc = msg->desc;
// initialize myself with contents of this message.
...
}
break;
case OTHER_MSG:
{
interface *self = static_cast<interface*>(myself);
...
}
break;
....
}
}
Then your CreateNewGadget code does:
void *ptr = malloc(some_amount);
msg newmsg;
newmsg.type = NEW_MSG;
// other fields
some_message_handler(ptr, &msg);
// now we have an initialized object, that we can add to our tree or whatever.
The less horrible version is more like this:
struct gadgetinterface {
virtual ~gadgetinterface() {}
virtual void handle_message(msg *) = 0;
};
struct mygadget : gadgetinterface {
void handle_message(msg *m) {
// no need for NEW_MSG, just do other messages
}
};
gadgetinterface *mygadget_factory(some parameters) {
// use some parameters, either passed to constructor or afterwards
return new mygadget();
}
Then we register a pointer to mygadget_factory with the gadget manager, and CreateNewGadget does this:
gadgetinterface *some_factory(some parameters); // that's it!
Where some_factory is the function pointer that was registered, so in the case of this gadget type, it points to mygadget_factory.

Lua, C++, and poor man's subclassing

I'm lead dev for Bitfighter, and we're working with a mix of Lua and C++, using Lunar (a variant of Luna, available here) to bind them together.
I know this environment does not have good support for object orientation and inheritance, but I'd like to find some way to at least partially work around these limitations.
Here's what I have:
C++ Class Structure
GameItem
|---- Rock
|---- Stone
|---- RockyStone
Robot
Robot implements a method called getFiringSolution(GameItem item) that looks at the position and speed of item, and returns the angle at which the robot would need to fire to hit item.
-- This is in Lua
angle = robot:getFiringSolution(rock)
if(angle != nil) then
robot:fire(angle)
end
So my problem is that I want to pass rocks, stones, or rockyStones to the getFiringSolution method, and I'm not sure how to do it.
This works for Rocks only:
// C++ code
S32 Robot::getFiringSolution(lua_State *L)
{
Rock *target = Lunar<Rock>::check(L, 1);
return returnFloat(L, getFireAngle(target)); // returnFloat() is my func
}
Ideally, what I want to do is something like this:
// This is C++, doesn't work
S32 Robot::getFiringSolution(lua_State *L)
{
GameItem *target = Lunar<GameItem>::check(L, 1);
return returnFloat(L, getFireAngle(target));
}
This potential solution does not work because Lunar's check function wants the object on the stack to have a className that matches that defined for GameItem. (For each object type you register with Lunar, you provide a name in the form of a string which Lunar uses to ensure that objects are of the correct type.)
I would settle for something like this, where I have to check every possible subclass:
// Also C++, also doesn't work
S32 Robot::getFiringSolution(lua_State *L)
{
GameItem *target = Lunar<Rock>::check(L, 1);
if(!target)
target = Lunar<Stone>::check(L, 1);
if(!target)
target = Lunar<RockyStone>::check(L, 1);
return returnFloat(L, getFireAngle(target));
}
The problem with this solution is that the check function generates an error if the item on the stack is not of the correct type, and, I believe, removes the object of interest from the stack so I only have one attempt to grab it.
I'm thinking I need to get a pointer to the Rock/Stone/RockyStone object from the stack, figure out what type it is, then cast it to the correct thing before working with it.
The key bit of Lunar which does the type checking is this:
// from Lunar.h
// get userdata from Lua stack and return pointer to T object
static T *check(lua_State *L, int narg) {
userdataType *ud =
static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
if(!ud) luaL_typerror(L, narg, T::className);
return ud->pT; // pointer to T object
}
If I call it thusly:
GameItem *target = Lunar<Rock>::check(L, 1);
then the luaL_checkudata() checks to see if the item on the stack is a Rock. If so, everything is peachy, and it returns a pointer to my Rock object, which gets passed back to the getFiringSolution() method. If there is a non-Rock item on the stack, the cast returns null, and luaL_typerror() gets called, which sends the app off into lala land (where the error handling prints a diagnostic and terminates the robot with extreme prejudice).
Any ideas on how to move forward with this?
Many thanks!!
Best solution I've come up with... ugly, but works
Based on the suggestions below, I came up with this:
template <class T>
T *checkItem(lua_State *L)
{
luaL_getmetatable(L, T::className);
if(lua_rawequal(L, -1, -2)) // Lua object on stack is of class <T>
{
lua_pop(L, 2); // Remove both metatables
return Lunar<T>::check(L, 1); // Return our object
}
else // Object on stack is something else
{
lua_pop(L, 1); // Remove <T>'s metatable, leave the other in place
// for further comparison
return NULL;
}
}
Then, later...
S32 Robot::getFiringSolution(lua_State *L)
{
GameItem *target;
lua_getmetatable(L, 1); // Get metatable for first item on the stack
target = checkItem<Rock>(L);
if(!target)
target = checkItem<Stone>(L);
if(!target)
target = checkItem<RockyStone>(L);
if(!target) // Ultimately failed to figure out what this object is.
{
lua_pop(L, 1); // Clean up
luaL_typerror(L, 1, "GameItem"); // Raise an error
return returnNil(L); // Return nil, but I don't think this
// statement will ever get run
}
return returnFloat(L, getFireAngle(target));
}
There are probably further optimizations I can do with this... I'd really like to figure out how to collapse this into a loop because, in reality, I will have a lot more than three classes to deal with, and this process is a bit cumbersome.
Slight improvement on the above solution
C++:
GameItem *LuaObject::getItem(lua_State *L, S32 index, U32 type)
{
switch(type)
{
case RockType:
return Lunar<Rock>::check(L, index);
case StoneType:
return Lunar<Stone>::check(L, index);
case RockyStoneType:
return Lunar<RockyStone>::check(L, index);
default:
displayError();
}
}
Then, later...
S32 Robot::getFiringSolution(lua_State *L)
{
S32 type = getInteger(L, 1); // My fn to pop int from stack
GameItem *target = getItem(L, 2, type);
return returnFloat(L, getFireAngle(target)); // My fn to push float to stack
}
Lua helper function, included as a separate file to avoid user needing to add this manually to their code:
function getFiringSolution( item )
type = item:getClassID() -- Returns an integer id unique to each class
if( type == nil ) then
return nil
end
return bot:getFiringSolution( type, item )
end
User calls this way from Lua:
angle = getFiringSolution( item )
I think you're trying to do the method dispatch in the wrong place. (This problem is symptomatic of a difficulty with all of these "automated" ways of making Lua interact with C or C++: with each of them, there's some magic going on behind the scenes, and it's not always obvious how to make it work. I don't understand why more people don't just use Lua's C API.)
I had a look at the Lunar web pages, and it looks to me as if you need to create a methods table on type T and then call the Luna<T>::Register method. There's a simple example on the web. If I'm reading the code correctly, none of the glue code in your question is actually the recommended way of doing things with Lunar. (I'm also assuming that you can implement these methods entirely as C++ calls.)
This is all pretty dodgy because the documentation on Lunar is thin.
A sensible alternative would be to do all the work yourself, and just associate each C++ type with a Lua table containing its methods. Then you have the Lua __index metamethod consult that table, and Bob's your uncle. Lunar is doing something close to these, but it's sufficiently dressed up with C++ templates that other goo that I'm not sure how to make it work.
The template stuff is very clever. You might want either to take the time to understand deeply how it works, or to reconsider if and how you want to use it.
Summary: for each class, make an explicit methods table, and register each class using the Lunar Register method. Or roll your own.
You should tell us what exactly does not work in your code. I suppose that it is Lunar<Rock>::check(L, 1) that fails for all non-Rocks. Am I correct?
Also it would be fine if you specified which version of Lunar you use (a link to it would be great).
If it is this one, then class type is stored in the Lua object metatable (one may say that this metatable is the type).
Looks like the simplest way to check if object is a Rock without patching Lunar is to call luaL_getmetatable(L, Rock::className) to get class metatable and to compare it with lua_getmetatable(L, 1) of your first argument (note luaL in the first function name). This is a bit hackish, but should work.
If you fine with patching Lunar, one of possible ways is to add some __lunarClassName field to the metatable and store T::name there. Provide lunar_typename() C++ function (outside of the Lunar template class -- as we do not need T there) then, and return from it the value of that __lunarClassName field of argument's metatable. (Do not forget to check if object has metatable and that metatable has such field.) You may check Lua object type by calling lunar_typename() then.
A bit of advice from personal experience: the more of business logic you push to Lua, the better. Unless you're pressed by severe performance constraints, you probably should consider to move all that hierarchy to Lua -- your life would become much simpler.
If I may help you further, please say so.
Update: The solution you've updated your post with, looks correct.
To do the metatable-based dispatch in C, you may use, for example, a map of integral lua_topointer() value of the luaL_getmetatable() for a type to a function object/pointer which knows how to deal with that type.
But, again, I suggest to move this part to Lua instead. For example: Export type-specific functions getFiringSolutionForRock(), getFiringSolutionForStone() and getFiringSolutionForRockyStone() from C++ to Lua. In Lua, store table of methods by metatable:
dispatch =
{
[Rock] = Robot.getFiringSolutionForRock;
[Stone] = Robot.getFiringSolutionForStone;
[RockyStone] = Robot.getFiringSolutionForRockyStone;
}
If I'm right, the next line should call the correct specialized method of robot object.
dispatch[getmetatable(rock)](robot, rock)
I suggest that you define an object oriented system in pure lua, and then write a custom binding to C++ for that aspect of the API.
Lua is well suited for prototype OO implementations, where tables are used for emulating classes, in which one entry has a function called new, which when called returns an appropriate table of the same 'type'.
From C++, however, make a LuaClass that has a .invoke method, accepting a C string (ie, a null-terminated const char array) to specify the name of the member function you want to call, and depending on how you want to handle variable arguments, have several templated versions of this .invoke method for zero, one, two, ... N arguments as neccessary, or define a method of passing a variable number of arguments into it, and there are many ways to do that.
For Lua, I suggest making two .invoke methods, one which expects an std::vector, and another that expects an std::map, but I'll leave that up to you. :)
In my last Lua/C++ project, I used only null-terminated arrays of C-strings, requiring lua to convert the string to an appropriate value.
Enjoy.
I was facing quite the same needs, and here is what I came up with.
(I had to do some minor changes to the Lunar header)
First, I've added a global "interface" for all the classes that will contains Lua methods.
I understand this could appear less flexible than the "original" way, but in my opinion it's clearer, and I do need it to perform dynamic casts.
class LuaInterface
{
public:
virtual const char* getClassName() const=0;
};
Yes, it only contains one pure virtual method, which will obviously return the static "className" attribute in the derived classes. That way, you can have polymorphism, with keeping this static name member needed by the templated lunar classes.
To make my life easier, I've also added some defines :
#define LuaClass(T) private: friend class Lunar<T>; static const char className[]; static Lunar<T>::RegType methods[]; public: const char* getClassName() const { return className; }
So you basically just have to declare a class like this :
class MyLuaClass: public LuaInterface
{
LuaClass(MyLuaClass)
public:
MyLuaMethod(lua_State* L);
};
Nothing particular here.
I also need a "singleton" (ouch, I know : it doesn't really have to be a singleton just do whatever you feel like to)
class LuaAdapter
{
//SINGLETON part : irrelevant
public:
const lua_State* getState() const { return _state; }
lua_State* getState() { return _state; }
template <class T>
void registerClass(const std::string &name)
{
Lunar<T>::Register(_state);
_registeredClasses.push_back(name);
}
void registerFunction(const std::string &name, lua_CFunction f)
{
lua_register(_state, name.c_str(), f);
_registeredFunctions.push_back(name);
}
bool loadScriptFromFile(const std::string &script);
bool loadScript(const std::string &script);
const StringList& getRegisteredClasses() const { return _registeredClasses; }
const StringList& getRegisteredFunctions() const { return _registeredFunctions; }
LuaInterface* getStackObject() const;
private:
lua_State* _state;
StringList _registeredClasses;
StringList _registeredFunctions;
};
For now, just look at the registerClass method : we store its name here in a StringList (just a list of string)
Now, the idea is to implement a proxy to register our classes :
template<class _Type>
class RegisterLuaClassProxy
{
public:
RegisterLuaClassProxy(const std::string &name)
{
LuaAdapter::instance()->registerClass<_Type>(name);
}
~RegisterLuaClassProxy()
{
}
};
We need to build one instance of each proxy for each LuaInterface class.
ie: in MyClass.cpp, after the standard "Lunar" method declaration :
RegisterLuaClass(MyClass)
With, again, a couple of defines :
#define RegisterLuaClassWithName(T, name) const char T::className[] = name; RegisterLuaClassProxy<T> T ## _Proxy(name);
#define RegisterLuaClass(T) RegisterLuaClassWithName(T, #T)
Do the same with the "functions" methods/proxy.
Now some little changes in the Lunar header :
remove the "userdataType" structure from the class, and define a single struct outside the class :
typedef struct { LuaInterface *pT; } userdataType;
(note that you will also need to add some static_cast inside the Lunar class)
Well, well. Now we have all the structures we need to perform our operation, I've defined it in the getStackObject() method of my LuaAdapter, based on your code.
LuaInterface* LuaAdapter::getStackObject() const
{
lua_getmetatable(_state, 1);
for(StringList::const_iterator it = _registeredClasses.begin(); it != _registeredClasses.end(); ++it)
{
// CHECK ITEM
luaL_getmetatable(_state, it->c_str());
if(lua_rawequal(_state, -1, -2)) // Lua object on stack is of class <T>
{
lua_pop(_state, 2); // Remove both metatables
userdataType *ud = static_cast<userdataType*>(luaL_checkudata(_state, 1, it->c_str()));
if(!ud) luaL_typerror(_state, 1, it->c_str());
return ud->pT;
}
else // Object on stack is something else
{
// Remove <T>'s metatable, leave the other in place for further comparison
lua_pop(_state, 1);
}
}
return NULL;
}
Here is the trick : since the returned pointer points to an abstract class, you can safely use dynamic_cast<> with it. And add some "intermediate" abstract classes, with nice virtual methods, like :
int fire(lua_State *L)
{
GameItem *item = dynamic_cast<GameItem*>(LuaAdapter::instance()->getStackObject());
if( item!= NULL)
{
item->fire();
}
return 0;
}
... I Hope this will help. Don't hesitate to correct me / add stuff / feedback.
Cheers :)