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
the
appropriate C++ member functions.  There are some elegant workarounds,
such
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
Lua
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
distribution
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
would
likely be nonportable, too) that could do the global, member
non-virtual,
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.

Thanks,
Josh

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

http://web.archive.org/web/20010404033818/www.bestweb.net/~rhickey/

Download from:

http://web.archive.org/web/20010404033818/http://www.bestweb.net/~rhicke
y/callback.zip

---------------------------------
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
n);

---------------------------------
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;
  lua_lock(L);
  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);
  api_incr_top(L);
  lua_unlock(L);
}


---------------------------------
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
{
public:
	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()");