Lua cmodule编写

    《Programming in Lua》里面提到“扩展扩展Lua的基本方法之一就是为应用程序注册新的C函数到Lua中去”,还有“当你打算使用C函数来扩展Lua的时候,即使你仅仅只想注册一个C函数,将你的C代码设计为一个库是个比较好的思想:不久的将来你就会发现你需要其他的函数”。

    学习这部分用去了我几个小时来实践。我想把我遇到的问题和解决方法和大家分享一下。没准什么时候我自己都忘了怎么做,那时候还可以用这篇文章回忆一下。

    我是跟着书学的,这里难免copy一些书上的东西。因为思路还没有整理好,可能会讲得比较乱。还会提到一些不太相关的问题。

    我先用VC++ 2005建了一个空项目,在里面添加了一个文件:ldirlib.c。文件内容贴一下吧。

#include <stdio.h>
#include <string.h>
#include <stddef.h>

#include <windows.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#define DIRLIB_API __declspec(dllexport)

#include "ldirlib.h"

static int l_dir(lua_State *L)
{
 WIN32_FIND_DATA FindFileData;
 HANDLE          hFind = INVALID_HANDLE_VALUE;
 char            DirSpec[MAX_PATH] = {0};  // directory specification
 DWORD           dwError = 0;
 char            szErrMsg[256] = {0};
 int             i = 1;

 const char *path = luaL_checkstring(L, 1); // 检查路径

 strncpy(DirSpec, path, strlen(path));
 strncat(DirSpec, "\\*", 3); // 添加通配符

 hFind = FindFirstFile(DirSpec, &FindFileData);
 dwError = GetLastError();

 if (hFind == INVALID_HANDLE_VALUE) {
  _snprintf(szErrMsg, sizeof(szErrMsg), "Invalid file handle. Error is %u", dwError);
  lua_pushnil(L);
  lua_pushstring(L, szErrMsg);
  return 2;
 } else {
  lua_newtable(L);
  while (FindNextFile(hFind, &FindFileData) != 0) {
   lua_pushnumber(L, i++);
   lua_pushstring(L, FindFileData.cFileName);
   lua_settable(L, -3);
  }

  dwError = GetLastError();
  FindClose(hFind);
  if (dwError != ERROR_NO_MORE_FILES) {
   _snprintf(szErrMsg, sizeof(szErrMsg), "Error is %u", dwError);
   lua_pop(L, 1); // 弹出栈顶的表
   lua_pushnil(L);
   lua_pushstring(L, szErrMsg);
   return 2;
  }
 }

 return 1;
}

static const struct luaL_reg dir_funcs[] = {
    {"dir", l_dir},
    {NULL, NULL}  /* sentinel */
};

DIRLIB_API int luaopen_dirlib(lua_State *L) {
    luaL_register(L, LUA_DIRLIBNAME, dir_funcs);
    return 1;
}

    l_dir这个函数的内容基本上是我从MSDN上抄来的,功能是列出指定路径下的文件和目录。因为只是供学习之用,我也没考虑可移植性。

    就像书上说的,第一步,定义库函数,我这里是l_dir;第二步,声明一个数组,保存所有的函数和他们对应的名字,我这里是dir_funcs;第三步,定义一个函数,该函数可以被成为“loader”,用来将前面数组中的C函数注册到Lua中去,我这里是luaopen_dirlib。头文件ldirlib.h内容如下:

#ifndef LDIRLIB_H_
#define LDIRLIB_H_

#define LUA_DIRLIBNAME "dirlib"

#ifdef DIRLIB_API
/*do nothing*/
#else
#define DIRLIB_API __declspec(dllimport)
#endif

DIRLIB_API int luaopen_dirlib(lua_State *L);

#endif

    头文件定义了库名字。DIRLIB_API用于输出函数。

    然后,我修改项目设置,将代码编译成DLL。

    那么,在哪里使用这个DLL?虽然我学了两个星期Lua了,但是脑子里面还是乱麻一团,没有清晰的认识。可能是因为这阵子经常熬夜的缘故吧。我就这么考虑了一番,在Lua代码里面,可以直接使用math.sin(1)、os.exit()这样的方法调用标准库提供的函数,这些函数的本质也是C函数。使用lua命令执行Lua程序的时候,解释器应该已经自动加载了标准库。标准库的实现方式和上面介绍的基本一致。那么我刚才编译的DLL算第三方库吧,用来扩展Lua的,那就是在Lua程序里面调用了。那怎么使用这个DLL呢?书上介绍的是这样的,

mylib = package.loadlib("fullname-of-your-library","luaopen_mylib")

    第一个参数是库文件的完整路径,第二个参数是用于打开库的函数的名字。我打开一个DOS窗口,执行lua解释器。输入如下内容,

func = package.loadlib("F:\\Lua\\luaApplication\\ExtendLuaWithCLib\\Release\\ExtendLuaWithCLib.dll", "luaopen_dirlib")

    package.loadlib这个函数所做的工作是这样的。先调用ll_register,在registry中以第一个参数添加前缀LIBPREFIX宏作为key查找是否存在value。如果未找到,就创建一个userdata(const void *类型),然后取registery["_LOADLIB"]的metatable,再将这个它设置为刚创建的userdata的metatable,然后在registry中添加一个域,这个域以库完整路径添加前缀LIBPREFIX宏作为key,以userdata作为value。如果ll_register没加载成功,调用ll_load,这个函数的实现是根据操作系统的不同而不同的,在windows系统下调用LoadLibraryA加载DLL。如果加载成功,再调用ll_sym,这个函数的实现也是根据操作系统的不同而不同的,在windows系统下调用GetProcAddress。最后将C函数压栈。我们得到了func,它也就是luaopen_dirlib。你必须再执行这个函数func,才能真正加载库。再输入如下内容,

func()

    这样就打开了我写的库。执行func时做的工作主要是建立一个表,表名就是luaL_register函数的第二个参数,这里是LUA_DIRLIBNAME,即库名。设置该表的一个域,key是上面介绍的结构数组中每个结构的第一个值(Lua程序中使用的函数名),value就是结构的第二个值(真正的C函数)。可以参考Lua加载标准库的过程。接下来,我输入了如下内容,

t = dirlib.dir("c:\\")

    这就是在Lua程序中调用dir的方法,给它传的参数是表示路径的字符串,注意“\”要用“\\”表示。根据Lua参考手册和Lua语言的源代码来看,在Lua程序中,给Lua标准库中的函数传什么参数是由对应的C函数的实现决定的。dir函数对应的C函数是l_dir,它的实现里需要一个参数,就是路径,它的返回值是一个表。所以如果你要写一个第三方库来扩展Lua,你必须提供文档,说明库函数的原型。根据l_dir这个函数的实现可以知道,它在栈中放入一个表,这个表的内容大概是这样:{[1]="a", [2]="b", ...}。然后我输入如下内容,

for _, v in ipairs(t) do

print(v)

end

    窗口里面打印出一列文件和目录的名称。当然了,你也可以把在窗口中输入的内容整理到一个文件中,直接在命令行模式下用“Lua”命令执行这个文件。毕竟都是Lua代码嘛。

    说到这里,大部分内容已经说完了。

    再多说几句。其实,用C函数扩展Lua,就是添加Lua函数。在C应用中也可以调用Lua函数,所以你用于扩展Lua的C函数也可以间接的在C应用中调用。Lua就是这样,可以用C函数扩展Lua,Lua可以嵌入到C应用中。Lua本身是用C语言实现的,那么当你将Lua嵌入到C应用中的时候,也就是在用C间接调用C,中间层就是Lua。跳出来看,或者说从整体去把握,会有不同的感悟。

    以上的想法、做法都是特定于我自己的开发环境。如果你的开发环境和我的不一样,我提供的方法仅供参考。你要尝试不同方法来得到解决问题的办法,多加努力。

    能力有限,有错难免,望广大网友不吝赐教,多谢多谢。

    如需转载,请标明出处。

http://www.cppblog.com/lai3d/archive/2008/11/11/66662.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值