Skip to main content
GameDev.net gamedev.net
🔒 Locked

Exposing object instance to Lua

Started by medevilenemy Apr 1, 2011 at 4:13 AM 24 replies 5.6k views
Original Post
medevilenemy
medevilenemy
Hello all, I'm thinking of using tolua++ to bind some of my internal engine functionality to lua (so that scripts can do things like set up triggers and stuff with ease), but it seems the tolua++ documentation is fairly mediocre. Specifically, what I want to do is expose an object instance of my trigger handler class (TriggerHandler triggers;) so that I could do things like triggers.newtrigger()) from within a lua script. Does tolua++ provide a mechanism to do this? It seems fairly straight forward to expose the TriggerHandler class itself, but the documentation doesn't say anything about object instances. Thanks!
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
MeshGearFox
MeshGearFox
Pass them in as lightuserdata, maybe..?
medevilenemy
medevilenemy
I know SWIG is capable of binding C++ variables (in this case, object instances) with lua objects (If I understand correctly, the lua variable functions as a proxy for the C++ one, allowing normal usage). Not sure if tolua++ can do that sort of thing. I don't want to simply pass in the object by value, but rather expose it to direct manipulation in lua (more accurately, direct interface through its member functions)
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
owl
owl
I've made a journal entry briefly describing SWIG if u feel like reading it.
[size="2"]I like the Walrus best.
medevilenemy
medevilenemy
@Owl: Your journal post is actually what got me looking into SWIG/toLua++/etc in the first place . Unfortunately SWIG doesn't seem to work quite the way I'd prefer (I'd rather like something that will generate code which can simply be #included, opened in the lua state, and available without having to compile anything separately or whatever. The documentation for all these things seems a little weird so its possible I'm missing something.
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
owl
owl
Please let me know if you find something simpler/better. SWIG is the easiest way for newbies (like me) I've found.
[size="2"]I like the Walrus best.
medevilenemy
medevilenemy
It certainly seemed fairly simple... is there a way to use the code it generates without having to separately compile it and link it in?
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
owl
owl
To be able to use the code it generates first you have to generate it with swig (letting it generate the cpp from the interface file) and in order to use the resulting code in your program you have to include the cpp file into your project.

Now if you mean to be able to use the code it generates by your own, I'm pretty sure you can, but you'd have to understand it first.
[size="2"]I like the Walrus best.
medevilenemy
medevilenemy
If all I have to do is #include the code SWIG generates, that sounds pretty good to me
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
owl
owl
It's simpler than that, you don't have to #include the cpp, you just add it to your project like any other of the cpp you write yourself. That will allow you to use anything you declare in the interface file (classes, types, etc) from your Lua scripts. Remember that every time you change c++ code you are exposing to Lua you must re-generate the cpp.
[size="2"]I like the Walrus best.
medevilenemy
medevilenemy
Alrighty then, thanks
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
Zakwayda
Zakwayda
If you're interested in other options, you might look at Luabind.
medevilenemy
medevilenemy
I'm going to play around a little with SWIG, and see if that suits my purposes. If it doesn't, then I'll go back to looking into the various other options. I'll be quite happy if I can, as I said initially, expose my triggers object and call its functions.
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
medevilenemy
medevilenemy
I've spent the past couple days looking into options, particularly those listed on http://lua-users.org/wiki/BindingCodeToLua and I am uncertain what to use. I attempted to use SWIG, but I had trouble compiling it / trouble getting it to play nice with my build setup (an admittedly messy set of headers in a wxdev-c++ project). Consequently, I prefer something in the form of a prebuilt library that I can link in like SLB or luabridge. SLB is problematic because I don't know how to build it (it is designed to be built with cmake, which I am unfamiliar with). luabridge looks rather nice but I can't tell if it supports some mechanism to expose an object from C++ (like my TriggerHandler triggers;) to lua (so I can call its functions from lua). Any thoughts? Thanks for all the suggestions so far.
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
owl
owl
SWIG should just work out of the box, at least the windows binary distribution (swigwin) did for me. You MUST set the installation directory in the PATH environment variable so it can find it's own modules when processing the interface file. I've had ZERO problems with it and I'm exposing boost, std among with my own classes.
I got another entry on my journal regarding SWIG if you wanna read it.
[size="2"]I like the Walrus best.
medevilenemy
medevilenemy
Using the binary distribution, I was able to get the utility proper to work, but I was unable to get the code it generated to compile in properly (and once I tweaked it enough to compile it didn't work -- though that could very well have been because I was uncertain how to initialize it, or because I may have been trying to expose my object incorrectly -- I simply specified it in the interface file). Part of the problem, I'm sure, is that my code isn't split into separate header-source pairs, rather my stuff in this project is for the most part right in the headers themselves. Consequently, things aren't compiled as separate modules and linked together, rather they are all compiled into one blob.
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
owl
owl
The only time I have problems compiling the cpp when building the project is when I've made some change in my source files and I haven't re-generated the cpp using the swig command.

The steps must always be:

- Change your source code
- Change your .i file
- run swig.exe on your .i file to generate the cpp
- build your project
[size="2"]I like the Walrus best.
medevilenemy
medevilenemy
I'll perhaps give it another try, a couple questions relevant to that:
1) How do I properly initialize SWIG from the C++ side? (presumably there is a function that needs calling once I've opened my lua state
2) How do I expose the object I have mentioned (as that is really the whole point of this exercise right now)
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.
owl
owl
We are talking about Lua 5.1.4 or newer.
In my interface file (.i) the first line declares the name of the module all the c++ stuff I expose will lie into:

[font="Courier New"]%module owl[/font]

which means that in the Lua scripts everything I expose must be preceded by "owl." owl.myFunction, owl.myClass

Then, in your c++ code, when you initialize Lua (You need to link liblua.a or lua.lib of course) you initialize also the module your defined in the interface file by calling a function that will be named luaopen_YOURMODULENAME(luaState*). Note the extern int luaopen_owl(lua_State* L); clause in the extern "C" bit. Mine is owl, yours will be whatever you call it.




extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
extern int luaopen_owl(lua_State* L);
}

void init()
{

L = lua_open();
luaL_openlibs(L);
luaopen_owl(L); // load the wrappered module
}


Also if you're going to use winmain instead of main, you gotta do the following otherwise instead of your program what will run will be the Lua interpreter:

#include <windows.h>


extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
extern int luaopen_owl(lua_State* L);
}

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);

int main(int argc, char **argv)
{
return WinMain(GetModuleHandle(0),0,GetCommandLine(),0);
}

// #include "sumthing.h"

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
return 0;
}



All this information I gathered from the available documentation on the SWIG and Lua sites and related forums. I'm sharing it with you because it's still fresh in my mind. Still you'll need to read some yourself and understand the little you still got to do by yourself. Otherwise any help I can bring will be useless.
[size="2"]I like the Walrus best.
medevilenemy
medevilenemy
I already have a functional lua state which can execute scripts at will and whatnot (a few weeks ago I manually exposed my Dispatch_Message function to lua, and I've had some fun with that . Anyway, I didn't know about luaopen_MODULENAME... the swig documentation isn't terribly clear. many thanks, I'll post my results when I'm done.

EDIT: I have tried it a different way, but it now crashes my program. Here is the function I'm manually binding


int luabind_VoidP(lua_State *L)
{
int argc = lua_gettop(L);
int temp1 = 0;
float temp2 = 0;
void *toreturn;

if(argc != 1)
toreturn = NULL;
if(lua_isnumber(L, 1) == 1) // If this is a number
{
const char *tmpstr = lua_tostring(L, 1);
int slen = strlen(tmpstr);

if(tmpstr[slen-1] == '0' && tmpstr[slen-2] == '.') // Is this a quoted float ending in .0
{
temp2 = (float)lua_tonumber(L, 1);
toreturn = ftov(temp2);
}
else
{
temp2 = (float)lua_tonumber(L, 1);
if(fmod(temp2, 1) == 0) // If this looks like an integer
{
temp1 = (int)temp2;
toreturn = (void *)temp1;
}
else // If this is actually a float
{
toreturn = ftov(temp2);
}
}
}
else if(lua_isstring(L, 1) == 1) // If this is a char or a string
{
toreturn = (void *)lua_tostring(L, 1);
}

lua_pushlightuserdata(L, toreturn);
return 1;
}


the function is called like trigs.triggers:AddNew(mids.HEARTBEAT, VoidP(10), NULL, NULL, NULL, NULL) where each of the arguments is ostensibly a void pointer.
There was a saying we had in college: Those who walk into the engineering building are never quite the same when they walk out.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.