[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: RE: Registering methods with lua_register
- From: "Paul Dixon" <paul@...>
- Date: Mon, 31 Jan 2000 13:24:28 -0000
> 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 as a closure parameter.
This works great if you 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.
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
class 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 and opens up lots of 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 COGENT_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[]=
{
COGENT_FUNCTION(ExampleFunc1)
COGENT_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);
};