Using SWIG to connect C++ to Lua
I closed off my last post about adding Lua to an SDL application saying I’d look at a tool to do the bindings for me. I actually already had something in mind for the job. I looked at a couple but SWIG seems to be the most versatile. It’s not tied just to Lua so the work I put in to learning how to use SWIG will also apply partly to any other scripting languages I want to support. Any within the realm that SWIG supports, of course. Notably missing from the list so far is Javascript. Scheme is represented in the form of Guile (which is a specific Scheme interpreter as far as I understand). The documentation for SWIG is pretty sizable but of course with all the combinations of host & script language out there they aren’t all explained clearly enough for a beginner like me. So I started to experiment. SWIG focuses on wrappers for C, C++ support seems more complex. So to understand things I had to scale back even from my simple SDL C++ test program then scaled back up once I got some communication between code written in both languages. What I came up with was that I had to write a separate interface file (SimpleGraphics.i) then use swigwin to read that file and create a wrapper for my class. The wrapper gets the default name of SimpleGraphics_wrap.cxx. I added the wrapper file as a source file in my Visual Studio project then the wrapper gets compiled with the rest of the project. Here's the header for the class in question #include "SDL.h" #include "SDL_image.h" /** @author Rob */ class SimpleGraphics{ private: static SDL_Surface *_s; int _x; int _y; Uint8 _r; Uint8 _g; Uint8 _b; public: SimpleGraphics(); ~SimpleGraphics(); static SetSurface( SDL_Surface *surface ); void goToXY(int x, int y); void setColour(Uint8 R, Uint8 G, Uint8 B); void lineRel(int deltaX, int deltaY); void DrawLine(int x1, int y1, int x2, int y2); void DrawLine(int x1, int y1, int x2, int y2, Uint8 R, Uint8 G, Uint8 B); int DrawImage( char *image_path, int x_pos, int y_pos ); void putpixel(int x, int y, Uint8 R, Uint8 G, Uint8 B); Uint32 getpixel(int x, int y); }; Based on the simple examples in the SWIG tutorial (I count myself in the truly lazy), I had hoped I could just include the header in the interface file (SimpleGraphics.i) like the following. #ifdef SWIG %module simple %{ #include "SimpleGraphics.h" %} #endif This did not work. I tried a lot of variations on that theme but got nowhere with it. What did work was copying the contents of the header to the interface (that is from SimpleGraphics.h to SimpleGraphics.i) and using the %inline directive. My current version of SimpleGraphics.i looks like this. %module simple %inline %{ typedef unsigned char Uint8; class SimpleGraphics{ public: SimpleGraphics(); ~SimpleGraphics(); void goToXY(int x, int y); void setColour(Uint8 R, Uint8 G, Uint8 B); void lineRel(int deltaX, int deltaY); void DrawLine(int x1, int y1, int x2, int y2); void DrawLine(int x1, int y1, int x2, int y2, Uint8 R, Uint8 G, Uint8 B); int DrawImage( char *image_path, int x_pos, int y_pos ); void putpixel(int x, int y, Uint8 R, Uint8 G, Uint8 B); }; %} I dropped all the private stuff that SWIG can't use anyway and I added a typedef for the Uint8 type that I use from SDL. Without the %inline the default directive is %header. According to the docs
The %inline directive inserts all of the code that follows verbatim into the header portion of an interface file. The code is then parsed by both the SWIG preprocessor and parser.
Since the interface is copied from the header and the wrapper is created from the interface, it's important to remember that every time the header changes (that is, the interface to the class changes) the SWIG interface file has to be adjusted accordingly and the wrapper has to be created again using swigwin. In a real project, these steps could be at least partially automated in the build process. The command line I used for swigwin was roughly C:\...\hilbert\src>swig -lua -I"c:\...\swigwin-1.3.31\Lib" -I"c:\...\swigwin-1.3.31\Lib\lua" -c++ SimpleGraphics.i The -I options add directories for include files since the default SWIG include path doesn't exist on my machine (of course the ... refer to the full path). The -c++ option indicates the host language is C++ and the -lua option indicates we need Lua bindings. This allowed me to run the a little Lua script that draws a line with the SimpleGraphics::DrawLine () method. sg = simple.SimpleGraphics() sg:DrawLine(0,0,100,500,10,10,10) After this it promptly crashes choking on something in the destructor. That's a problem for another day though.
2.875
Your rating: None Average: 2.9 (136 votes)