你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:技术专栏 / C专栏
Lua和C的那些事
 
 Lua和C是天生的好基友,语言开发者提供了一系列API,让他们通过栈进行交流。用Lua做游戏逻辑开发有些时日了,下面主要针对Lua C API的应用进行总结。
 
一、扩展Lua
 
        Lua核心很小,主要包含一个解释器,其他功能可以通过动态库的形式作为插件来扩展,io、string、math、table等内置库都是通过此方式来实现,只是他们被集成到了一个lua.dll中罢了。制作一个动态库形式的module,需要在代码中通过luaL_Reg数组指定lua function到c function的映射,接着实现c function,最后在luaopen_xxx(xxx为module name)注册这个luaL_Reg。这里给出一个非常简单的例子,它使用VC++创建一个Console DLL: www.2cto.com
 
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include <math.h>
 
int mysin (lua_State* L);
 
static const struct luaL_Reg mymathlib [] =
{
    { "sin" , mysin } ,
    { NULL , NULL }
};
 
static int mysin( lua_State* L )
{ www.2cto.com
    double d = luaL_checknumber( L , 1);
    lua_pushnumber(L , sin( d));
    return 1;
}
 
__declspec(dllexport) int luaopen_mymathlib(lua_State * L)
{
    luaL_register(L , "mymathlib" , mymathlib);
    return 1;
}
编译成dll后放到lua解释器目录下。Lua test code:
 
require "mymathlib"
 
local a = mymathlib.sin(0.5)
print(a)
 
二、作为脚本系统
 
        Lua应用最多的领域当属游戏开发,WOW的UI和插件让它名声大噪。在这种应用中,Lua作为应用程序的一个子系统,用作配置或者业务处理。在将Lua与应用集成起来时,必须用到Lua C API,根据其规范,你需要写一系列的static函数,作为Lua与应用程序的粘合代码。如果要在Lua使用C++对象,可将其作为userdata,为它创建一个metatable,并将粘合函数放入其中,关键是要让__index指向metatable自身,这样当Lua访问userdata的field时,__index会引导它去搜索metatable自身,从而获得注册的粘合函数。
 
        有很多开源的粘合代码生成器,他们都是在precompile时做了一些工作,因而不是使用macro就是template,这两种方法的代表是toLua++和luaBind。个人更倾向使用toLua++,一方面这种方式比较直白,另一方面本人弱于使用template。在WGAME中也将原来写得不是很好的bind代码替换成了toLua++,目前UI和关卡逻辑重度使用了Lua,产生的bind代码会有几万行,由于项目采用事件驱动的方式,在profile时看到对游戏整体性能影响非常小。luaBind大概了解过,没有在实际项目中用过,在此就不做评论了。
 
        在使用toLua++时我最好奇的是它对C++关键特性是如何支持的。除了上面说的成员函数外,对于多态的支持,它是通过在static函数后加编号,调用时判断参数是否对应来遍历找到正确static函数的;对于复杂成员变量,它会自动生成get/set方法;而继承关系,则是通过子类将父类作为metatable来实现。秉着重新发明车轮的精神,我试着写了一个简化的自动生成器[我在github上]。我定义了几个关键字作为类与方法的导出标识:
 
    {module_begin = "LUACBIND_MODULE_BEGIN" , module_end = "LUACBIND_MODULE_END" , method_begin = "LUACBIND_METHOD_BEGIN" , method_end = "LUACBIND_METHOD_END"}
 
    util.h定义了产生bind代码需要的宏,parser.lua对指定的.h文件进行扫描产生bind代码,在main函数中register后,就可以在lua中使用了。
 
三、调试器
 
        Lua的C API和Debug库提供了实现调试器的必要方法,对应了两种实现方式:一种是Remdebug所采用的,直接用lua实现;另外一种是使用C API。不管哪种方式,使用HOOK都是必须的,但使用Lua debug库会比C API更方便,因为不用考虑栈平衡问题。在用C API实现调试器时,可用lua_newthread创建一个coroutine,之后yield/resume/getstack/getlocal都作用它上面,breakpoint通常会采用在hook中yield的方式来实现,但不能等hook返回之后去进行栈回溯,因为traceexec根据hook mask调用对应hook函数后,如果state是为LUA_YIELD状态,将会调用luaD_throw,最终使用longjmp导致无法进行回溯。
 
        利用春节值班两天清闲时光,基于lua 5.2实现了一个命令行调试器[我在github上],目前仅有几个简单的功能:加载/运行lua脚本、设置/清除断点、单步、查看简单类型变量值,命令格式可参考README。
  推荐精品文章

·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089