Sam Nolan
Sam Nolan

Reputation: 94

OOP in Lua From C

I would like to implement Object Orientated Programming in my lua interpreter, I understand that I can return a lua table from a C Function. And I would like the table that is returned to be full of C functions.

player = getClosestPlayer();
player.walkTo();

where both getClosestPlayer() and walkTo() are C functions.

From the C function for walkTo(), how do I differentiate between object types?

I would prefer every object to have a gid that I can use to identify it (player.gid) but how do I access that gid from the c function?

In other words, what is the equivalent of self.gid from C code?

int l_playerWalkTo(lua_State* functionState){
    int gid = // self.gid?
    // do something with gid
}

One way I could do this is to upvalue every single function in the table, but is there a more elegant way to do it?

Upvotes: 1

Views: 565

Answers (2)

Sam Nolan
Sam Nolan

Reputation: 94

Many thanks to macroland for his answer, I just though I would clear up what he has said. This lua wrapper can be used to implement c++ classes into Lua: https://bitbucket.org/alexames/luawrapper/overview

A great example of the usage of this library can be found here: https://bitbucket.org/alexames/luawrapperexample/src/

Here is the code (taken straight from the example site)

Lua:

alicesaccount = BankAccount.new("Alice", 100)

alicesaccount:deposit(20);
alicesaccount:deposit(30);
alicesaccount:deposit(40);

c++:

BankAccount* BankAccount_new(lua_State *L)
{
    const char* owner = luaL_checkstring(L, 1);
    float balance = luaL_checknumber(L, 2);
    return new BankAccount(owner, balance);
}

int BankAccount_deposit(lua_State *L)
{
    BankAccount* account = luaW_check<BankAccount>(L, 1);
    float amount = luaL_checknumber(L, 2);
    account->deposit(amount);
    return 0;
}

static luaL_Reg BankAccount_table[] =
{
    { NULL, NULL }
};

static luaL_Reg BankAccount_metatable[] =
{
    { "deposit", BankAccount_deposit },
    { NULL, NULL }
};

int luaopen_BankAccount(lua_State* L)
{
    luaW_register<BankAccount>(L,
        "BankAccount",
        BankAccount_table,
        BankAccount_metatable,
        BankAccount_new // If your class has a default constructor you can omit this argument,
                        // LuaWrapper will generate a default allocator for you.
    );
    return 1;
}

As you can see, Using this method the first argument is an instance of the object

Upvotes: 1

macroland
macroland

Reputation: 1025

I had the similar problem and the way I resolved is:

1) Create an interface class in C++ with abstract methods

class LuaInterfaceOOP
{
public:
    LuaInterfaceOOP(){}
    virtual CObject* clone(void) const=0;
    virtual wxString type(void)=0;
    virtual wxString ToString(void)=0;
    wxString GetType()return this->type();
    wxString GetToString() return this->ToString();
    virtual ~CObject(){}
};

2) Any class that you want to expose to Lua should implement this to be consistent.

class MyClass: public LuaInterfaceOOP
{
 public:
 wxString type() { return "MyClass";}
 wxString ToString();
};

3) When you write a wrapper for this class make sure

int MyClass_toString(lua_State* L)
{
    MyClass* mc= luaW_check<MyClass>(L, 1);
    const char* str=mc->ToString().c_str();
    lua_pushstring(L, str);

    return 1;
}

int MyClass_type(lua_State* L)
{
    lua_pushstring(L,"MyClass");

    return 1;
}

4) Overload the type function provided by Lua, for you the important part will be:

case LUA_TUSERDATA:
{
    wxString str1;
    if(lua_getmetatable(L,idx)) // Stk: Userdata Table
    {
        lua_getfield(L,-1,"type");  // Stk: Userdata Table function
        if(!lua_pcall(L,0,1,0))     // Stk: Userdata Table string
        {
            str1<<lua_tostring(L,-1);
            wxReturnStr<<str1;
            lua_pop(L,2);// Stk: Userdata
        }
        else //stk: Userdata table
        {
            lua_pop(L,1);
            wxReturnStr<<"userdata"; //stk: Userdata
        }

    }else wxReturnStr<<"userdata";
    break;
}

EDIT 1: Adding code to wrap C++ funcs to Lua

static luaL_Reg MyClass_table[] = {
    { NULL, NULL }
};

static luaL_Reg Myclass_metatable[] = {  
    {"type",    Myclass_type},
    {"__tostring",      Myclass_toString},
    { NULL, NULL }
};

Finally,

static int luaopen_MyClass(lua_State* L)
{
    luaW_register<MyClass>(L, "MyClass", MyClass_table, MyClass_metatable, MyClass_new);
   return 1;
}

Now in Lua you can use an expression such as if(type(aclass)=="MyClass")

I am not sure if these steps are the best way, but so far it worked.

Upvotes: 0

Related Questions