Reputation: 3044
EDITED:
I'm trying to create a simple getter function based on variable name used in Lua.
For example, it can be used like the following in Lua.
num = 123
str = "hello"
print(my.getValue("num")); --> result: 123
print(my.getValue("str")); --> result: hello
And this is myBindings.h
file
static void getValue(const char *name, lua_State *L)
{
lua_getglobal(L, name);
switch (lua_type(L, -1))
{
case LUA_TBOOLEAN:
//return boolean
break;
case LUA_TNUMBER:
//return number
break;
case LUA_TSTRING:
//return string
break;
case LUA_TTABLE:
//return table
break;
default:
//return nil
break;
}
lua_pop(L, 1);
}
And this is myBindings.i
file.
%module my
%{
#include "myBindings.h"
%}
%include <stl.i>
%include <std_except.i>
%include <exception.i>
%include <typemaps.i>
%typemap(default) (lua_State *L)
{
$1 = L;
}
%include "myBindings.h"
How do I create a SWIG typemap so getValue
function returns various types in Lua?
Upvotes: 2
Views: 317
Reputation: 10939
There is one things wrong with the getValue
function. When you pop after the switch statement, you will pop whatever you pushed inside the switch statement. I guess the intention is rather to pop the global whose type you query off the stack. Therefore I just save the type in a local variable and pop the global immediately after. For the sake of this example I'm pushing some dummy values.
static void getValue(const char *name, lua_State *L)
{
lua_getglobal(L, name);
switch (lua_type(L, -1))
{
case LUA_TBOOLEAN:
lua_pushboolean(L, true);
break;
case LUA_TNUMBER:
lua_pushnumber(L, 3.14);
break;
case LUA_TSTRING:
lua_pushstring(L, "Hello world!");
break;
case LUA_TTABLE:
lua_newtable(L);
lua_pushstring(L, "value");
lua_setfield(L, -2, "key");
break;
default:
lua_pushnil(L);
break;
}
// Before we can return we have to clean up the stack. There is
// currently the global "name" and the return value on the stack in this order
//
// 1. Return value
// 2. "name"
//
// We cannot just lua_pop(L, 1) because that would remove the
// return value which we of course want to keep. To pop an
// element at a specific position we have to use lua_remove.
lua_remove(L, -2);
}
The interface file looks fine, but you have to notify SWIG that you pushed to the stack inside your C++ function and would like to return what you pushed. Thus you have to increment SWIG_arg
in an argout
typemap.
%module my
%{
#include "myBindings.h"
%}
%typemap(default) (lua_State *L) {
$1 = L;
}
%typemap(argout) (const char *name, lua_State *L) {
++SWIG_arg;
}
%include "myBindings.h"
Now we can check out the test script. In the first case we are calling getValue
with "num"
where num
has type number. Thus we expect to obtain 3.14
as this is what is pushed by the C++ function. In the second case we call with "str"
where str
is of type string. Thus we should obtain Hello world!
.
local my = require("my")
num = 123
str = "hello"
print(my.getValue("num"))
print(my.getValue("str"))
Let's try it out!
$ swig -lua -c++ test.i
$ clang++ -Wall -Wextra -Wpedantic -I /usr/include/lua5.2/ -fPIC -shared test_wrap.cxx -o my.so -llua5.2
$ lua5.2 test.lua
3.14
Hello world!
The in
typemap is wrong. numinputs = 0
tells SWIG that zero inputs are needed for a function with this signature. That's not true, because name
is passed as an argument. However, increasing numinputs = 1
forces you to check and convert the first argument yourself. Better let SWIG handle that and remove this argument from the typemap altogether.
%typemap(in, numinputs = 0) (void **p) {
$1 = nullptr;
}
The argout
typemap doesn't make any sense whatsoever and is not even valid C++. Even if you'd correct the invalid casts it would still be a huge type-unsafe memory leak.
Upvotes: 1