Sign in to follow this  
nerael

[Lua][LuaBind] Trouble with binding classes and methods

Recommended Posts

nerael    122
Hey GameDev. I've been implementing Lua for a game project, and I decided I wanted to try and use LuaBind to avoid having to write wrappers for everything I wanted to expose to script from C++. After finally getting everything to link successfully, I hit a snag when I try to expose a very basic class or method to lua. The strange thing is that I can get luabind to build fine in console examples like this that I found browsing tutorials and examples: Console cpp:
#include "lua_test.h"
using namespace luabind;

//classes pasted from headers
class NumberPrinter {
public:
	NumberPrinter(int number) :
	  m_number(number) {}

	  void print() 
	  {
		  cout << m_number << endl;
	  }

private:
	int m_number;
};

class A
{
private:
	int a_;
public:
	A() : a_(0) {}
	int get_a() const { return a_; }
	void set_a(int a) { a_ = a; }
};

void sayHello(int i) {
	cout << "hello " << i <<endl;
}

void retrieveData(A& a) {
	cout << "retrieved " << a.get_a() << endl;
}



int main ( int argc, char *argv[] )
{
	/* initialize Lua */
	LI = lua_open(); 
	
	if (NULL == LI)
	{
		printf("Error Initializing LUA!\n");
		return -1;
	}

	// load Lua base libraries
	luaL_openlibs( LI );
	
	//load in luabind
	open( LI );

	// Export our class with LuaBind
	//separate definitions with commas
		module(LI) 
		[
			class_<NumberPrinter>("NumberPrinter")
			.def(constructor<int>())
			.def("print", &NumberPrinter::print),

			class_<A>("A")
			.def(constructor<>())
			.property("a", &A::get_a, &A::set_a),
			def("sayHello",&sayHello),
			def("retrieveData",&retrieveData)

		];

The above works perfectly. However, in my own solution I'm trying to expose a method called BuildSpriteObj in the class 'Renderer2D' so that I can script out my sprite data. I've tried exposing just the method, and also the whole class. Here's what I'm trying to build: Snippet from GUIDemo solution:
#include "ScriptTranslator.h"
#include "Renderer2D.h"
#include "SpriteObj.h"

using namespace luabind;



//method implementations for script translator class

//initializes lua object and libraries
void ScriptTranslator::Init(lua_State* LI)
{
	//call LUA

	/* initialize Lua */
	LI = lua_open();

	if (NULL == LI)
	{
		//throw exception
	}

	/* load Lua base libraries */
	luaL_openlibs( LI );

	
	//load in luabind
	open( LI );

	//define luabind scopes and methods/classes registered
	module( LI ) //module is global scope
		[
			//bind spriteobj class so we can call buildspriteobj
			class_<Renderer2D>("Renderer2D")
			.def(constructor<>())
			.def("BuildSpriteObj", &Renderer2D::BuildSpriteObj)

			/*class_<SpriteObj>("SpriteObj")
			.def(constructor<>())
			.def("Init",&SpriteObj::Init)*/

			//def("BuildSpriteObj", &Renderer2D::BuildSpriteObj)

		];


}
Compiler Error when I attempt to expose the whole class: Error 2 error C2660: 'luabind::detail::overload_rep::overload_rep' : function does not take 2 arguments c:\documents and settings\administrator\desktop\gui demo\directx_template\references\luabind\include\luabind\class.hpp 1215 Compiler Error when I attempt to expose only one method: Error 2 error C2660: 'luabind::detail::free_functions::overload_rep::overload_rep' : function does not take 2 arguments c:\documents and settings\administrator\desktop\gui demo\directx_template\references\luabind\include\luabind\function.hpp 227 I've read over the (rather inadequate) documentation on the LuaBind site and I've browsed around for threads but nothing really turned up to explain this. I've looked into the method that breaks in the source files but it appeared fine, and I didn't want to start changing things within the luabind headers. I figure it's either in the way I've done the constructors, or some kind of screw-up in the binding code that I don't see. Has anyone seen this problem or have any idea what I'm doing wrong? Any and all input is greatly appreciated, thanks! Renderer2D Class Header:
#pragma once
#include <d3d9.h>
#include <d3dx9.h>

#include "cDLL_Node.h"
#include "cdll_list.h"

#include "ScriptTranslator.h"


#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

//forward declarations for contents of SpriteObj
struct SpriteTexture;
class SpriteObj;

class Renderer2D
{
	//attributes
	public:


	//linked lists of 2d stuff on screen
	cDLL_List<SpriteTexture>	SpriteTextures;
	cDLL_List<SpriteObj>		SpriteObjs;
	//cDLL_List<GuiObj>			GuiObjs;
	//cDLL_List<TextObj>			TextObjects;

	//D3D sprite object pointer
	ID3DXSprite*		m_pD3DSprite;
	IDirect3DDevice9*	m_pD3DDevice;

	//font
	LPD3DXFONT	textFont;



	Renderer2D();
	~Renderer2D();

	//behaviors

	//init
	void Init(IDirect3DDevice9*	m_pD3DDevice); //initialize lists i guess, and load gui objects

	//factory method that gets a filename, pass back a pointer to the finished picture, load picture
	//create sprite object, keep track of object to be deleted later

	//render
	void Render();	//directx class will tell 2d renderer to render, 
					//this method will contain loops that go through the lists to render everything

	//clear - clears the prototypes being used
	void Clear();

	//shutdown - shutdown D3Dspriteobject
	void Shutdown();

	//returns pointer to SpriteTexture when given the name of the picture filename
	SpriteTexture* GetSpriteTexturePointer(wstring picfilename);
	
	//this method is intended to be called from script, in order to build up SpriteObj prototypes
	void BuildSpriteObj(float positionx, float positiony, float rotation,
		float scaleX, float scaleY, int dirRot, int trans, 
		int red, int green, int blue, wstring picfilename );

};


Share this post


Link to post
Share on other sites
dw9    168
You probably need to redefine LUABIND_MAX_ARITY to something larger than the default (which is 10), since your function takes 11 parameters.

Share this post


Link to post
Share on other sites
nerael    122
Quote:
Original post by dw9
You probably need to redefine LUABIND_MAX_ARITY to something larger than the default (which is 10), since your function takes 11 parameters.


Yes, that has to be the problem, thanks! Would you happen to know if/where I can redefine that value without recompiling the static libraries? In the hpps that are in the includes I use don't seem to be able to see the definition of that variable, although its referenced quite a bit in them.

EDIT - I found the definition, it's done in the LuaBind 'config.hpp' file that's found in the includes. (for anyone else that might encounter this problem).

Upon changing the definition however (LUABIND_MAX_ARITY was 10 and I increased it to 15), I got back 600+ compiler errors related to the boost libraries 'tuples::tuple - too many template arguments'. I figure I have to change another define within boost that somehow correlates to the number of parameters I can pass through the template, but I'm still looking. Anyone know what it's about?

[Edited by - nerael on May 1, 2009 8:49:36 PM]

Share this post


Link to post
Share on other sites
ddn3    1610
Changing the max number of support arguments is more trouble than it's worth, why not pack those arguments into a struct and pass that instead. Luabind allows you to create bindings for simple structs in a snap and it wouldn't change your API that much.

Good Luck!

-ddn

Share this post


Link to post
Share on other sites
c0uchm0nster    182
Going along with ddn3's (good) suggestion, I'd also remind you that you can easily bind certain overloads of a function with luabind, so you don't necessarily have to change your engine at all.
Simply provide another version of the function that takes a struct of info (or multiple structs... your r g b values would be a good starting place), bind that version (and it's structs) to luabind, and have that version call your original 11 parameter version.

I've had to do this in a couple of situations with luabind, and I actually find myself using the "luabind version" in C++ faily often.

Share this post


Link to post
Share on other sites
nerael    122
Thank you everyone for your constructive input! I'll pack some structs together and see if I have better luck with the binding process over the next few days of development, along with utilizing some overloads where necessary. I came close to scrapping LuaBind and just writing my own wrappers in frustration today, haha. Hopefully I'll have some better luck with binding utilizing these suggestions. =D

Share this post


Link to post
Share on other sites
nerael    122
I've implemented these suggestions and it builds fine now! =D Now the only problem is to figure out how I can get these method calls to use the same instance of the object when it's called in script so it adds these sprites to the linked list in C++.

Share this post


Link to post
Share on other sites
nerael    122
Okay, so although my solution builds, I am having a real hard time trying to figure out how to get LuaBind to actually execute these C++ methods. Right now I just build to an empty screen, which means that my script file isn't executing properly (no sprites are being constructed). I know that there's no problem in my rendering code, because I can hardcode a sprite in by passing parameters to the BuildSpriteObj method manually in C++.

Ideally what I need to be able to do in this example, is to call a static method that builds the SpriteObj from the script file, so that in the Lua file I can just say StaticBuildSpriteObj(param,param,param... etc.);. Is this possible, or am I going to have to do it another way? As it stands I have the static method declared and passed to Lua as a non-member function, and I've tried figuring out how to use a copy of the Renderer2D class within Lua, but I can't get it back to C, and I figured out that I'd much rather use a static method if at all possible.

LUA Script File:




--R2D = Renderer2D();
--does this R2D object need to be declared?
--we're trying to use a singleton instance copy of the Renderer2D class
--can i use this instance for the purpose of holding the whole 2d renderer? or can i pass this back to c?
--can i instead pass the instance of the c class to lua so they use the same one?



--create a table that will hold all variables needed for a health display
--it will eventually be a spriteobj


HealthDisplay = {
positionx = 200.0,
positiony = 400.0,
scalex = 0.8,
scaley = 0.8,
rot = 0.0,
transp = 255,
red = 255,
green = 255,
blue = 255,
filepath = "healthbar.png"
};


--pack a struct with the argb values so it can be passed in the static build function
-- can we pass a typeless table into this thing as a struct?

--colors = ARGBValues(HealthDisplay.transp,HealthDisplay.red,HealthDisplay.green,HealthDisplay.blue);


--load the health display data into a spriteobj
--will luabind actually make this static function avaliable?

StaticBuildSpriteObj(

HealthDisplay.positionx,
HealthDisplay.positiony,
HealthDisplay.scalex,
HealthDisplay.scaley,
HealthDisplay.rot,
ARGBValues(HealthDisplay.transp,HealthDisplay.red,HealthDisplay.green,HealthDisplay.blue),
HealthDisplay.filepath
);




Updated Renderer2D.h

//Renderer2D.h
//Seraph Project


#pragma once
#include <d3d9.h>
#include <d3dx9.h>

#include "cDLL_Node.h"
#include "cdll_list.h"

#include "ScriptTranslator.h"


#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

//forward declarations needed
struct SpriteTexture;
struct ARGBValues;
class SpriteObj;

class Renderer2D
{
//attributes
public:

static Renderer2D* instance;



//linked lists of 2d stuff on screen
cDLL_List<SpriteTexture> SpriteTextures;
cDLL_List<SpriteObj> SpriteObjs;
//cDLL_List<GuiObj> GuiObjs;
//cDLL_List<TextObj> TextObjects;

//D3D sprite object pointer
ID3DXSprite* m_pD3DSprite;
IDirect3DDevice9* m_pD3DDevice;

//font
LPD3DXFONT textFont;



Renderer2D();
~Renderer2D();

//behaviors

//init
void Init(IDirect3DDevice9* m_pD3DDevice); //initialize lists i guess, and load gui objects

//factory method that gets a filename, pass back a pointer to the finished picture, load picture
//create sprite object, keep track of object to be deleted later

//render
void Render(); //directx class will tell 2d renderer to render,
//this method will contain loops that go through the lists to render everything

//clear - clears the prototypes being used
void Clear();

//shutdown - shutdown D3Dspriteobject
void Shutdown();

//returns pointer to SpriteTexture when given the name of the picture filename
SpriteTexture* GetSpriteTexturePointer(string picfilename);

//this method is intended to be called from script, in order to build up SpriteObj prototypes
void BuildSpriteObj(float positionx, float positiony, float rotation,
float scaleX, float scaleY,ARGBValues colors, string picfilename );

static void StaticBuildSpriteObj(float positionx, float positiony, float rotation,
float scaleX, float scaleY,ARGBValues colors, string picfilename);

static Renderer2D* GetInstance();

};




Updated Renderer2D.cpp

//Renderer2D.cpp
//Seraph Project



#include "Renderer2D.h"
#include "SpriteObj.h"
//#include "DirectX.h"


//required for singleton instance
Renderer2D* Renderer2D::instance = NULL;

//blank constructor
Renderer2D::Renderer2D()
{

}

Renderer2D::~Renderer2D()
{

}

void Renderer2D::Init(IDirect3DDevice9* m_pD3DDevice)
{
//remember to initialize linked lists here

//get the directx pointer object so we can build a SpriteObj
D3DXCreateSprite(m_pD3DDevice, &m_pD3DSprite);

//this makes the pointer contained in the r2d class point to the same device that the DirectX class has
this->m_pD3DDevice = m_pD3DDevice;

}

void Renderer2D::Render()
{

// get position and button status of mouse cursor
// calculate mouse cursor's effects on windows / send messages
// render all windows

//remember somewhere we need to start the d3d sprite object

//go through list of prototypes that we already have
SpriteObj* temp;

//tells SpriteTextures we're starting a new search
SpriteObjs.RestartIteration();


//grabs the next node in the list and makes sure its not null
//when the list meets the end, it will return null
for(temp=SpriteObjs.IterateNext(); temp != NULL; temp=SpriteObjs.IterateNext())
{
temp->Render(m_pD3DSprite);
}

// render mouse
// flip to screen


}

void Renderer2D::Shutdown()
{
//remember to shutdown linked lists
//pass true if asked for bool
}

void Renderer2D::Clear()
{
//wipes out everything from the screen, but keeping com objects
//used when shutting down and reinitializing linked lists
}


SpriteTexture* Renderer2D::GetSpriteTexturePointer( string picfilename )
{
//go through list of prototypes that we already have
SpriteTexture* temp;

//tells spritetextures we're starting a new search
SpriteTextures.RestartIteration();


//grabs the next node in the list and makes sure its not null
//when the list meets the end, it will return null
for(temp=SpriteTextures.IterateNext(); temp != NULL; temp=SpriteTextures.IterateNext())
{
//if we have one with the same picfilename, we return the pointer to that one
if(temp->picfilename == picfilename)
{
return temp;
}
}



//otherwise, get it from the file and add to prototype list
temp = new SpriteTexture();
temp->picfilename = picfilename;
//load sprite textures

//the picfile name only works in these arguments if its a regular string, and we call the magical c_str method
HRESULT HR = D3DXCreateTextureFromFileEx(m_pD3DDevice, picfilename.c_str(), 0, 0, 0, 0,
D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT,
D3DCOLOR_XRGB(255,0,255), //KEY COLOR (magenta)
&temp->ImageInfo, 0, &temp->m_pD3DTexture);

//add temp to list of prototypes
SpriteTextures.InsertNode(temp,true);

return temp;
}

void Renderer2D::BuildSpriteObj(float positionx, float positiony, float rotation,
float scaleX, float scaleY, ARGBValues colors, string picfilename )
{


//builds a new spriteobj
SpriteObj* temp = new SpriteObj( NULL, positionx, positiony, rotation,
scaleX, scaleY, colors);

//when setting parameters, we need to call the getspritetexturepointer
temp->m_pSpriteTexture = GetSpriteTexturePointer(picfilename);

//needs to add it to the list of spritobjs
SpriteObjs.InsertNode(temp,true);



}

void Renderer2D::StaticBuildSpriteObj( float positionx, float positiony, float rotation, float scaleX, float scaleY,ARGBValues colors, string picfilename )
{
GetInstance()->BuildSpriteObj( positionx, positiony, rotation, scaleX, scaleY, colors, picfilename);
}


//returns a pointer to the r2d instance
Renderer2D* Renderer2D::GetInstance()
{
if(instance)
{
return instance;
}

instance = new Renderer2D();

}







From Updated ScriptTranslator.cpp:

void ScriptTranslator::Init(lua_State* LI)
{
//call LUA

/* initialize Lua */
LI = lua_open();

if (NULL == LI)
{
//throw exception
}

/* load Lua base libraries */
luaL_openlibs( LI );


//load in luabind
open( LI );

//define luabind scopes and methods/classes registered
module( LI ) //module is global scope
[
//bind spriteobj class so we can call buildspriteobj
class_<Renderer2D>("Renderer2D")
.def(constructor<>())
.def("BuildSpriteObj", &Renderer2D::BuildSpriteObj),

def("StaticBuildSpriteObj", &Renderer2D::StaticBuildSpriteObj),

//pack arguments into structs so luabind has less to pass
class_<ARGBValues>("ARGBValues")
.def(constructor<int,int,int,int>())







/*class_<SpriteObj>("SpriteObj")
.def(constructor<>())
.def("Init",&SpriteObj::Init)*/


//def("BuildSpriteObj", &Renderer2D::BuildSpriteObj)

];

//attempt to pass the instance of the R2D object to lua as a global
//luabind::globals(LI)["R2D"] = &CDirectX::R2D;
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this