Pavel Vagin
Pavel Vagin

Reputation: 111

SWIG Lua: Extending (%extend) class with %native. Is it possible to add %native method?

%module test
class Foo{
public:
  Foo(); 
}

I want to have something like this:

%extend Foo{
%native(Bar) int Bar(lua_State * L);
}

Upvotes: 1

Views: 509

Answers (2)

Kaa
Kaa

Reputation: 705

In the .i file for your bindings, at the end include this code:

%wrapper %{
// This is code to add a new function to the object's metatable
void script_addNativeMethod(lua_State *L, const char *className, const char *methodName, lua_CFunction fn)
{
    SWIG_Lua_get_class_registry(L); /* get the registry */
    lua_pushstring(L, className);   /* get the name */
    lua_rawget(L,-2);               /* get the metatable itself */
    lua_remove(L,-2);               /* tidy up (remove registry) */

    // If the metatable is not null, add the method to the ".fn" table
    if(lua_isnil(L, -1) != 1)
    {
        SWIG_Lua_get_table(L, ".fn");
        SWIG_Lua_add_function(L, methodName, fn);
        lua_pop(L, 2);              /* tidy up (remove metatable and ".fn" table) */
    }
    else
    {
        printf("[script_addNativeMethod(..)] - \"%s\" metatable is not found. Method \"%s\" will not be added\n", className, methodName);
        return;
    }
}
%}

What this does is adds a new function to the wrapper CPP file called "script_addNativeMethod". You can call this function in the "init" binding code like so:

// Wrapper to add native Lua methods to existing C++ classes
%init %{
    script_addNativeMethod(L, "MetatableName", "methodName", /*int function(lua_State *L)*/function);
%}

Above all this, in the binding file you need to have the actual native lua function that you want to use as a method of your userdata:

%{
int function(lua_State *L)
{
    printf("Method called!\n");
    return 0;
}
%}

I practically just figured this out, and I wanted to post it here because this page ranks high in google and this is a pretty decent solution to get the job done. This needs to be done in every wrapper binding (*.i) file that you write with SWIG.

Good luck!

Upvotes: 1

Oliver
Oliver

Reputation: 29543

Lua does not have any true concept of method, just of tables of functions with some syntactic sugar so you can write Lua code that looks quite OO'ish:

foo = test.Foo() # will call the C++ Foo constructor and return a wrapped (Lua) Foo
myInt = foo:Bar()

When you write

myInt = foo:Bar()

Lua is actually executing

myInt = foo.Bar(foo)

which will cause Lua to look in the foo metatable for a function called Bar and will give it the foo instance as first parameter. Therefore what you would have to do is more along the lines of the following pseudocode (not tested, there are likely syntax errors, wrong order of parameters etc but hopefully you get the idea):

%native(Bar) int Bar(lua_State * L);

%{
int Bar(lua_State*L) {
  // there should be one arg on the stack: the Foo instance
  Foo* foo = (Foo*)<get void* from L stack>
  int answer = foo.Bar();
  // push answer onto L stack
  lua_pushnumber(L, answer);
  return 1;
}
%}

%luacode {
  test.Foo.Bar = test.Bar
}
...
%}

The %luacode makes Bar available as part of Foo "class", although I'm a little rusty in that area, you might have to add Bar the the Foo metatable instead, or do it from C (see section 5.6 of the SWIG user guide for sections of .i file where you could try to do this).

Curious to know if that works.

Upvotes: 0

Related Questions