Mon 12 Nov 2007
Ooops - A SWIG Interface Correction
Filed in Scripting, Programming, C++, Windows, Lua, HowToI glossed over the fact that my program crashed at the end of the script in my post on hooking up Lua to a C++ application. I new at the time I posted that there was a memory problem in calling the SimpleGraphics destructor but what I didn’t realize was that the problem was actually caused by the difference in the declaration of the SimpleGraphics class between the header file that the C++ program sees and the declaration that the SWIG-generated file sees. Here’s the one that gets compiled in to the C++ application:
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);
};
And here’s the one that I put in the SWIG interface file (SimpleGraphics.i)
%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);
};
%}
At the time I thought it was okay to leave out the private members of the C++ class. This was a mistake. The fact is that the generated wrapper (SimpleGraphics_wrap.cxx) has all the code to instantiate a SimpleGraphics object and I do exactly that in the Lua script
sg = simple.SimpleGraphics()
So the code this calls inside the SWIG-generated wrapper creates a SimpleGraphics object according to one definition. Then the constructor and DrawLine() member functions of that object actually use the other definition of that class. They modify private data members of the object. The space for these private data members was never allocated when the object was created from memory on the heap. So the debugger correctly spots damage to the no-man’s land after the end of the object when the destructor is called and the memory is freed. Thank-you debugger, and shame on me for not recognizing this sooner.
I’ll have to go back to that earlier post and make the corrections, hopefully it’s still pretty readable. I’ve always said this blog is for drafts though, we all get to learn together
. Now that I realize the entire class definition has to be available to SWIG in order to generate correct wrappers, I also need a definition of SDL_surface. Instead of duplicating more headers into my interface file, I found that I can use the header directive to get the SDL header included. Now my corrected interface file looks like this:
%module simple
%header %{
#include "SDL.h"
%}
%inline %{
typedef unsigned char Uint8;
class SimpleGraphics{
private:
static SDL_Surface *_s;
int _x;
int _y;
Uint8 _r;
Uint8 _g;
Uint8 _b;
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);
};
%}
The one thing that bothers me is that I still need the typedef for Uint8 in there. It should be visible through the SDL.h include. Without the typedef the code compiles but doesn’t do what it should or generate any errors. For the time being I’ll leave that be but I’m in clean-up mode now so I’m going to add some error-handling code for the Lua interpreter as well as other error-checking that needs to be done. After that maybe I can find a good way to set up the entire source for download from my blog.
Oh, and since this is a mea culpa post, another important bit that I neglected in my original post was that the initialization function for the SWIG module has to be visible to main() in this project. So I missed the bit
extern "C"
{
int Simple_Init(lua_State* L);
}
This is of course necessary in order to call Simple_Init() function from main() in the code I showed the other day. Without the extern “C” part in a C++ file, this generates an error like
hilbert.obj : error LNK2019: unresolved external symbol "int __cdecl Simple_Init(struct lua_State *)" (?Simple_Init@@YAHPAUlua_State@@@Z) referenced in function _SDL_main
c:\...\hilbert\vs2003\hilbert\Debug\hilbert.exe : fatal error LNK1120: 1 unresolved externals
This is because of the difference in function signatures between C and C++ which the extern “C” convention is meant to fix.
I’ll patch up the old post soon and hopefully have some more updates on this later on in the week. Or I’ll lose interest and drift off to something else.