lua-users home
lua-l archive

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

I've always had to hack my Lua callback C functions to get them to call
appropriate C++ member functions.  There are some elegant workarounds,
as using Luna, but I decided to bite the bullet and just make it appear
as if
Lua directly supported more C++ style functionality.

The purpose of this article is simply to demonstrate a facility whereby
callbacks may continue to be global (or static members), as they are
now, but
also can appear under the guise of both non-virtual and virtual member
functions of ANY class.

I'm not suggesting this support be inserted into the main Lua
since the C++ nature of the code would most certainly break Lua's
portability.  It would be cool to find a standard C approach (which
likely be nonportable, too) that could do the global, member
and member virtual callbacks this patch does.  Either way, I find it
sufficient to just add the support to my personal Lua environments.  You
may, too.


Step 1: Download Functor Library
I don't know where Rich Hickey's current functor library is anymore, but
is archived at:

Download from:

Step 2: Make Lua compile for C++
Rename all .c files to .cpp in src/, src/lib, src/lua, and src/luac.
Lua compiles
fine with .cpp files under Visual C++ 6 and 7.

Step 3: Callback.hpp
Copy the unzipped Callback.hpp to include/.

If on VC6/7, comment out line 264:
//#define RHCB_CANT_OVERLOAD_ON_CONSTNESS //of mem funcs

Step 4: lua.h
To lua.h, add:

typedef int (*lua_CFunction) (lua_State *L);  // Already exists.

#include "Callback.hpp"
typedef CBFunctor1wRet<lua_State*, int> LuaFunctor;

inline LuaFunctor* LuaFunctorType()
	return 0;

LUA_API void  lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
// Already exists.
LUA_API void  lua_pushcfunctorclosure (lua_State *L, LuaFunctor fn, int

Step 5: lobject.h
To lobject.h, change Closure to look like:

typedef struct CClosure {
  lu_byte isC; // 0 for Lua functions, 1 for C functions
  lu_byte nupvalues;
  lu_byte marked;
  union Closure *next;
  lua_CFunction f;
  lu_byte f2[sizeof(LuaFunctor)]; // LuaFunctor f2;
  TObject upvalue[1];
} CClosure;

Step 5: lapi.cpp
To lapi.cpp, add:

#include <new.h>	// Or wherever your placement new is.

LUA_API void lua_pushcfunctorclosure (lua_State *L, LuaFunctor fn, int
n) {
  Closure *cl;
  api_checknelems(L, n);
  cl = luaF_newCclosure(L, n);
  cl->c.f = NULL;
  ::new(&cl->c.f2) LuaFunctor;
  LuaFunctor* functor = (LuaFunctor*)&cl->c.f2;
  *functor = fn;
  L->top -= n;
  while (n--)
    setobj(&cl->c.upvalue[n], L->top+n);
  setclvalue(L->top, cl);

Step 6: Compile/Run/Happiness!
Add the following function and class to lua.cpp:

static int LS_LOG(lua_State* L)
	printf("In static function\n");
	return 0;

class Logger
	int LS_LOG(lua_State* L)
		printf("In member function\n");
		return 0;
	virtual int LS_LOGVIRTUAL(lua_State* L)
		printf("In virtual member function\n");
		return 0;

Add the following test to function main() in lua.cpp:

  lua_pushcfunctorclosure(L, makeFunctor(LuaFunctorType(), LS_LOG), 0);
  lua_setglobal(L, "LOG");
  lua_dostring(L, "LOG()");

  Logger logger;
  lua_pushcfunctorclosure(L, makeFunctor(LuaFunctorType(), logger,
Logger::LS_LOG), 0);
  lua_setglobal(L, "LOG2");
  lua_dostring(L, "LOG2()");

  lua_pushcfunctorclosure(L, makeFunctor(LuaFunctorType(), logger,
Logger::LS_LOGVIRTUAL), 0);
  lua_setglobal(L, "LOG3");
  lua_dostring(L, "LOG3()");