lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


[Apologies for posting this twice - I read through it after posting it and
found a few things I wanted to correct...]



> I am trying to register a C++ object method to be called from the
> lua enviroment...

I wanted to do something similar - have lua code call non-static member
functions. You can do this in a fairly neat way with closures. Define a
non-static function that takes a function name, and register that function
under different names, using the required function name or other id as a
closure parameter.

This works great if your class is single-instance, for classes where you
have multiple instances, you could have lua pass a this pointer as the first
parameter to each function (this technique was demonstrated by David Jeske
in a previous reply).

In the code below, the key points are as follows:

1) CLua::s_FuncMap maps function names onto member function pointers
2) we keep our single instance ptr in s_pThis
3) The CLua constructor registers each function as a closure that actually
   calls CLua::DespatchFunction with the required function as the 1st param
4) CLua::DespatchFunction is a static member that looks up the relevant
   non-static member function and calls it through s_pThis

You can achieve the same thing by defining and registering a static member
that calls the non-static through s_pThis, but this method improves
maintainability if you have lots of non-static functions, and opens up a few
other possibilities.

I've simplied this code as much as possible to make the general idea
clearer. With a bit of work you could build a multiple instance class, or
class that can serve as a general base class for this sort of thing. The
closure parameter could also be simply an index into the s_FuncMap array,
which would remove the need for searching in DespatchFunction.



/////////////////////////////////////
// example.cpp (example.h is below!)

#include "example.h"

//one and only class instance
CLua* CLua::s_pThis=NULL;

//function table
#define MAP_FUNCTION(name) {#name, CLua::name},

//new functions are added simply by writing a new member
//function and including it in this table
FunctionMap CLua::s_FuncMap[]=
{
	MAP_FUNCTION(ExampleFunc1)
	MAP_FUNCTION(ExampleFunc2)
	{NULL, NULL}
};


CLua::CLua()
{
	//Register functions. Each function is registered as a
	//call to DespatchFunction, giving a closure which features
	//the real function name. The despatch function ensure the
	//correct member gets called. This makes it easy to use
	//non-static member functions as handlers

	for (int n=0; s_FuncMap[n].pszName; n++)
	{
		lua_pushstring((LPSTR)s_FuncMap[n].pszName);
		lua_pushcclosure(CLua::DespatchFunction, 1);
		lua_setglobal((LPSTR)s_FuncMap[n].pszName);
	}

    //store our one-and-only ptr
    ASSERT(s_pThis==NULL)
    s_pThis=this;
}

CLua::~CLua()
{
    ASSERT(s_pThis==this)
    s_pThis=NULL;
}

//non static function despatcher
void CLua::DespatchFunction(void)
{
	ASSERT(s_pThis);

	//function name is first parameter, passed as
	//as closure
	LPCSTR pszFunc=lua_getstring(lua_getparam(1));

	for (int n=0; s_FuncMap[n].pszName; n++)
	{
		if (lstrcmp(s_FuncMap[n].pszName, pszFunc)==0)
		{
			//call function
			(s_pThis->*s_FuncMap[n].pfnHandler)();
			return;
		}
	}

	//unknown function name
	ASSERT(FALSE);
}


//example function - note that we need to know how many
//parameters to skip before we get the 'real' parameters
void CLua::ExampleFunc1(void)
{
	CString str;
	int nParam=CLOSURE_PARAMS+1;
	for (lua_Object obj=lua_getparam(nParam);
           obj!=LUA_NOOBJECT;
           obj=lua_getparam(++nParam))
	{
		str+=lua_getstring(obj);
	}

	MessageBox(NULL, str, "ExampleFunc1", MB_OK);
}





/////////////////////////////////////
// example.h

//typedef of a non-static lua callback
typedef void (CLua::* LuaFunction)(void);

//maps function names onto member function pointers
struct FunctionMap
{
	LPCSTR pszName;
	LuaFunction pfnHandler;
};

//count of closure params
const int CLOSURE_PARAMS=1;


//example class
class CLua
{
public:
    CLua();
    virtual ~CLua();

protected:
	static CLua* s_pThis;
	static FunctionMap s_FuncMap[];


//members called by DespatchFunction
protected:
	void ExampleFunc1(void);
	void ExampleFunc2(void);

//statics called by LUA
protected:
	static void DespatchFunction(void);
};