函数调用lua学习2:在cpp中调用lua

查了好多资料,发现还是不全,干脆自己整理吧,至少保证在我的做法正确的,以免误导读者,也是给自己做个记录吧!

    描述:

    luna(请看SimplerCppBinding是一个很好的从lua调用c++函数的例子。但是你如果想从c++中调用lua函数,那么你须要保存你的lua函数到,一个注册了的子表中或者一个全局的表的子表中,然后就能够在c++代码中用lua_pcall去调用这个函数。以下的例子是用Lunar(请看CppBindingWithLunar),这是一个luna的改进版本,为了便利一点使用。

    lunar.h for lua 5.0

extern "C" {
#include "lua.h"
#include "lauxlib.h"
}

template <typename T> class Lunar {
	typedef struct { T *pT; } userdataType;
	public:
	typedef int (T::*mfp)(lua_State *L);
	typedef struct { const char *name; mfp mfunc; } RegType;

	static void Register(lua_State *L) {
		lua_newtable(L);
		int methods = lua_gettop(L);

		luaL_newmetatable(L, T::className);
		int metatable = lua_gettop(L);

		// store method table in globals so that
		// scripts can add functions written in Lua.
		lua_pushvalue(L, methods);
		set(L, LUA_GLOBALSINDEX, T::className);

		// hide metatable from Lua getmetatable()
		lua_pushvalue(L, methods);
		set(L, metatable, "__metatable");

		lua_pushvalue(L, methods);
		set(L, metatable, "__index");

		lua_pushcfunction(L, tostring_T);
		set(L, metatable, "__tostring");

		lua_pushcfunction(L, gc_T);
		set(L, metatable, "__gc");

		lua_newtable(L);                // mt for method table
		lua_pushcfunction(L, new_T);
		lua_pushvalue(L, -1);           // dup new_T function
		set(L, methods, "new");         // add new_T to method table
		set(L, -3, "__call");           // mt.__call = new_T
		lua_setmetatable(L, methods);

		// fill method table with methods from class T
		for (RegType *l = T::methods; l->name; l++) {
			lua_pushstring(L, l->name);
			lua_pushlightuserdata(L, (void*)l);
			lua_pushcclosure(L, thunk, 1);
			lua_settable(L, methods);
		}

		lua_pop(L, 2);  // drop metatable and method table
	}

	// call named lua method from userdata method table
	static int call(lua_State *L, const char *method,
			int nargs=0, int nresults=LUA_MULTRET, int errfunc=0)
	{
		int base = lua_gettop(L) - nargs;  // userdata index
		if (!luaL_checkudata(L, base, T::className)) {
			lua_settop(L, base-1);           // drop userdata and args
			lua_pushfstring(L, "not a valid %s userdata", T::className);
			return -1;
		}

		lua_pushstring(L, method);         // method name
		lua_gettable(L, base);             // get method from userdata
		if (lua_isnil(L, -1)) {            // no method?
			lua_settop(L, base-1);           // drop userdata and args
			lua_pushfstring(L, "%s missing method '%s'", T::className, method);
			return -1;
		}
		lua_insert(L, base);               // put method under userdata, args

		int status = lua_pcall(L, 1+nargs, nresults, errfunc);  // call method
		if (status) {
			const char *msg = lua_tostring(L, -1);
			if (msg == NULL) msg = "(error with no message)";
			lua_pushfstring(L, "%s:%s status = %d\n%s",
					T::className, method, status, msg);
			lua_remove(L, base);             // remove old message
			return -1;
		}
		return lua_gettop(L) - base + 1;   // number of results
	}

	// push onto the Lua stack a userdata containing a pointer to T object
	static int push(lua_State *L, T *obj, bool gc=false) {
		if (!obj) { lua_pushnil(L); return 0; }
		luaL_getmetatable(L, T::className);  // lookup metatable in Lua registry
		if (lua_isnil(L, -1)) luaL_error(L, "%s missing metatable", T::className);
		int mt = lua_gettop(L);
		subtable(L, mt, "userdata", "v");
		userdataType *ud =
			static_cast<userdataType*>(pushuserdata(L, obj, sizeof(userdataType)));
		if (ud) {
			ud->pT = obj;  // store pointer to object in userdata
			lua_pushvalue(L, mt);
			lua_setmetatable(L, -2);
			if (gc == false) {
				lua_checkstack(L, 3);
				subtable(L, mt, "do not trash", "k");
				lua_pushvalue(L, -2);
				lua_pushboolean(L, 1);
				lua_settable(L, -3);
				lua_pop(L, 1);
			}
		}
		lua_replace(L, mt);
		lua_settop(L, mt);
		return mt;  // index of userdata containing pointer to T object
	}

	// get userdata from Lua stack and return pointer to T object
	static T *check(lua_State *L, int narg) {
		userdataType *ud =
			static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
		if(!ud) {
			luaL_typerror(L, narg, T::className);
			return NULL;
		}
		return ud->pT;  // pointer to T object
	}

	private:
	Lunar();  // hide default constructor

	// member function dispatcher
	static int thunk(lua_State *L) {
		// stack has userdata, followed by method args
		T *obj = check(L, 1);  // get 'self', or if you prefer, 'this'
		lua_remove(L, 1);  // remove self so member function args start at index 1
		// get member function from upvalue
		RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
		return (obj->*(l->mfunc))(L);  // call member function
	}

	// create a new T object and
	// push onto the Lua stack a userdata containing a pointer to T object
	static int new_T(lua_State *L) {
		lua_remove(L, 1);   // use classname:new(), instead of classname.new()
		T *obj = new T(L);  // call constructor for T objects
		push(L, obj, true); // gc_T will delete this object
		return 1;           // userdata containing pointer to T object
	}

	// garbage collection metamethod
	static int gc_T(lua_State *L) {
		if (luaL_getmetafield(L, 1, "do not trash")) {
			lua_pushvalue(L, 1);  // dup userdata
			lua_gettable(L, -2);
			if (!lua_isnil(L, -1)) return 0;  // do not delete object
		}
		userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
		T *obj = ud->pT;
		if (obj) delete obj;  // call destructor for T objects
		return 0;
	}

	static int tostring_T (lua_State *L) {
		char buff[32];
		userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
		T *obj = ud->pT;
		sprintf(buff, "%p", (void*)obj);
		lua_pushfstring(L, "%s (%s)", T::className, buff);

		return 1;
	}

	static void set(lua_State *L, int table_index, const char *key) {
		lua_pushstring(L, key);
		lua_insert(L, -2);  // swap value and key
		lua_settable(L, table_index);
	}

	static void weaktable(lua_State *L, const char *mode) {
		lua_newtable(L);
		lua_pushvalue(L, -1);  // table is its own metatable
		lua_setmetatable(L, -2);
		lua_pushliteral(L, "__mode");
		lua_pushstring(L, mode);
		lua_settable(L, -3);   // metatable.__mode = mode
	}

	static void subtable(lua_State *L, int tindex, const char *name, const char *mode) {
		lua_pushstring(L, name);
		lua_gettable(L, tindex);
		if (lua_isnil(L, -1)) {
			lua_pop(L, 1);
			lua_checkstack(L, 3);
			weaktable(L, mode);
			lua_pushstring(L, name);
			lua_pushvalue(L, -2);
			lua_settable(L, tindex);
		}
	}

	static void *pushuserdata(lua_State *L, void *key, size_t sz) {
		void *ud = 0;
		lua_pushlightuserdata(L, key);
		lua_gettable(L, -2);     // lookup[key]
		if (lua_isnil(L, -1)) {
			lua_pop(L, 1);         // drop nil
			lua_checkstack(L, 3);
			ud = lua_newuserdata(L, sz);  // create new userdata
			lua_pushlightuserdata(L, key);
			lua_pushvalue(L, -2);  // dup userdata
			lua_settable(L, -4);   // lookup[key] = userdata
		}
		return ud;
	}
};

#define LUNAR_DECLARE_METHOD(Class, Name) {#Name, &Class::Name}
    每日一道理
因为自信,在呀呀学语时,我靠着纤嫩的双腿,迈出人生的第一步;因为自信,我一次次将第一名的奖状高高举起;因为自信,我毫不吝惜地剪掉飘逸的长发,在运动场上展现风采……感谢自信,它给了我一双翅膀,让我在电闪雷鸣中去飞翔,在风雨中去搏击人生!

    account.cc C++ code

extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}

#include "lunar.h"

class Account {
	lua_Number m_balance;
	public:
	Account(double balance=0)    : m_balance(balance) { }
	void deposit(double amount)  { m_balance += amount; }
	void withdraw(double amount) { m_balance -= amount; }
	double balance(void)         { return m_balance; }
	~Account() { printf("deleted Account (%p)\n", this); }

	// Lua interface
	Account(lua_State *L) :     m_balance(luaL_checknumber(L, 1)) { }
	int deposit (lua_State *L) { deposit (luaL_checknumber(L, 1)); return 0; }
	int withdraw(lua_State *L) { withdraw(luaL_checknumber(L, 1)); return 0; }
	int balance (lua_State *L) { lua_pushnumber(L, balance()); return 1; }

	static const char className[];
	static Lunar<Account>::RegType methods[];
};

const char Account::className[] = "Account";

#define method(class, name) {#name, &class::name}

Lunar<Account>::RegType Account::methods[] = {
	method(Account, deposit),
	method(Account, withdraw),
	method(Account, balance),
	{0,0}
};

static int report (lua_State *L, int status)
{
	if (status) {
		const char *msg = lua_tostring(L, -1);
		if (msg == NULL) msg = "(error with no message)";
		fprintf(stderr, "ERROR: %s\n", msg);
		lua_pop(L, 1);
	}
	return status;
}

static int application (lua_State *L)
{
	lua_settop(L, 0);
	lua_pushliteral(L, "_TRACEBACK");
	lua_gettable(L, LUA_GLOBALSINDEX);   // get traceback function
	int tb = lua_gettop(L);

	Account a;
	Account *b = new Account(30);

	int A = Lunar<Account>::push(L, &a);
	int B = Lunar<Account>::push(L, b, true);

	lua_pushliteral(L, "a");
	lua_pushvalue(L, A);
	lua_settable(L, LUA_GLOBALSINDEX);

	lua_pushliteral(L, "b");
	lua_pushvalue(L, B);
	lua_settable(L, LUA_GLOBALSINDEX);

	lua_pushvalue(L, A);
	lua_pushnumber(L, 100.00);
	report(L, Lunar<Account>::call(L, "deposit", 1, 0, tb) < 0);
	lua_pushvalue(L, A);
	report(L, Lunar<Account>::call(L, "show",    0, 0, tb) < 0);
	lua_pushvalue(L, B);
	report(L, Lunar<Account>::call(L, "show",    0, 0, tb) < 0);

	lua_pushliteral(L, "main");
	lua_gettable(L, LUA_GLOBALSINDEX);
	report(L, lua_pcall(L, 0, 0, tb));

	return 0;
}

int main (int argc, char *argv[])
{
	lua_State *L = lua_open();

	luaopen_base(L);
	luaopen_table(L);
	luaopen_io(L);
	luaopen_string(L);
	luaopen_math(L);
	luaopen_debug(L);

	Lunar<Account>::Register(L);

	if (argc>1) {
		printf("loading '%s'\n", argv[1]);
		if (report(L, luaL_loadfile(L, argv[1]) || lua_pcall(L, 0, 0, 0)) == 0) {
			printf("running application\n");
			if (report(L, lua_cpcall(L, &application, 0)) == 0) {
				printf("okay\n");
			}
		}
	}

	lua_setgcthreshold(L, 0);  // collected garbage
	printf("close\n");
	lua_close(L);
	printf("done\n");
	return 0;
}

    编译代码

    这个代码用lua5.0编译方法如下:

g++ -o test  account.cc -L/usr/local/lib -llua -llualib

    Lua Test Code

function printf(...) io.write(string.format(unpack(arg))) end

function Account:show()
	printf("Account balance = $%0.02f\n", self:balance())
	end

	parent = {}

	function parent:rob(amount)
	amount = amount or self:balance()
self:withdraw(amount)
	return amount
	end

	getmetatable(Account).__index = parent

function main()
	print('a =', a)
	print('b =', b)
	print('metatable =', getmetatable(a))
	print('Account =', Account)
table.foreach(Account, print)

	a:show() a:deposit(50.30) a:show() a:withdraw(25.10) a:show()
debug.debug()
	end

    测试结果

$ ./test setup.lua
loading 'setup.lua'
running application
Account balance = $100.00
Account balance = $30.00
a =     Account (0x22fcb0)
b =     Account (0xa041d98)
metatable =     table: 0xa044f28
Account =       table: 0xa044f28
show    function: 0xa0464a0
balance function: 0xa0455f8
withdraw        function: 0xa045300
deposit function: 0xa045508
new     function: 0xa044fe8
Account balance = $100.00
Account balance = $150.30
Account balance = $125.20
lua_debug> a:show()
Account balance = $125.20
lua_debug> b:show()
Account balance = $30.00
lua_debug> b:deposit(0.01)  b:show()
Account balance = $30.01
lua_debug> c = Account(19.99)  c:deposit(b:rob())  c:show()
Account balance = $50.00
lua_debug> b:show()
Account balance = $0.00
lua_debug> b = nil
lua_debug> print('c =', c)
c =     Account (0xa048cf8)
lua_debug> cont
deleted Account (0x22fcb0)
okay
deleted Account (0xa041d98)
close
deleted Account (0xa048cf8)
done

    译自:

    http://lua-users.org/wiki/CallingLuaFromCpp

    

    

    

    

文章结束给大家分享下程序员的一些笑话语录: 程序员的愿望
  有一天一个程序员见到了上帝.上帝: 小伙子,我可以满足你一个愿望.程序员: 我希望中国国家队能再次打进世界杯.
  上帝: 这个啊!这个不好办啊,你还说下一个吧!
  程序员: 那好!我的下一个愿望是每天都能休息6个小时以上.
  上帝: 还是让中国国家打进世界杯.

你可能感兴趣的:(lua)