刚刚学习lua,学习到如何将C/C++函数接口注册到lua环境中,使之可以在lua被调用。
在http://www.lua.org/ftp/点击打开链接处下载lua-5.1.5源码
使用C语言写一个base64编码的函数接口,函数参数为输入字符串,输入字符串长度,输出字符串长度。返回值为编码后的字符串
注册到lua的方式有两种,一种是lua解释器如果支持动态链接,使用动态链接机制,将函数接口编译成动态链接库,然后将动态链接库放到lua的C路径(LUA_CPATH)中(例如可以放在跟lua源码文件同一目录下),然后直接使用require "base64",这句话会使自己编写的动态链接库链接到lua,并寻找luaopen_base64,将模块注册到package.loaded中。另一种方式是直接将自己写好的接口函数与lua源文件一起重新编译生成一个新的lua解释器,使用新的解释器代替旧的。此外,还需要以某种方式来告诉解释器,它应在打开新状态的同时打开这个模块,最简单的做法是将luaopen_base64加到luaL_openlibs会打开的标准库列表中,这个列表在文件linit.c中。
1.使用动态链接库方式将接口注册到lua环境
源码:
// base64.cpp : 定义 DLL 应用程序的导出函数。
//
#include <stddef.h>
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
char code[65] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
'='
};
char* trans(const char* str, unsigned int num)
{
char* res = (char*)malloc(sizeof(char) * 4);
unsigned char* s = (unsigned char*)str;
*res = *s >> 2;
*(res + 1) = ((*(s + 1) >> 4) + (*(s) << 4)) & 0x3f;
*(res + 2) = ((*(s + 1) << 2) + (*(s + 2) >> 6)) & 0x3f;
*(res + 3) = *(s + 2) & 0x3f;
return res;
}
char transform(char s)
{
return code[s];
}
char* encode(const char* str, unsigned int num, unsigned int* resnum)
{
int numtmp = num;
int count = 0;
char* temp = NULL;
char* res = NULL;
char* point = NULL;
int i;
if((numtmp % 3) != 0)
numtmp = (numtmp / 3 + 1) * 3;
count = (numtmp / 3) * 4;
temp = (char*)malloc(numtmp * sizeof(char));
res = (char*)malloc(count * sizeof(char));
memset(temp, 0, numtmp * sizeof(char));
memset(res, 0, count * sizeof(char));
memcpy(temp, str, num);
for(i = 0; i < numtmp / 3; i++)
{
point = trans((temp + i * 3), 3);
memcpy((res+i*4), point, 4);
}
if((numtmp - num) == 1)
{
*(res + count - 1) = 64;
}
else if((numtmp - num) == 2)
{
*(res + count - 1) = 64;
*(res + count - 2) = 64;
}
else
{
//none
}
for(i = 0; i < count; i++)
{
*(res + i) = transform(*(res + i));
}
*resnum = count;
return res;
}
static int lbase64(lua_State* L)
{
const char* str = lua_tostring(L, 1);
unsigned int num = lua_tointeger(L, 2);
unsigned int count = 0;
char* res = encode(str, num, &count);
if(res)
{
lua_pushlstring(L, res, count);
lua_pushinteger(L, count);
return 2;
}
else
{
lua_pushnil(L);
return 1;
}
}
static const struct luaL_Reg base64[] = {
{"lbase64", lbase64},
{NULL, NULL}
};
__declspec(dllexport) int luaopen_base64(lua_State* L)
{
luaL_register(L, "base64", base64);
return 1;
}
}
编译环境要包含lua源码库,然后将以上源码编译成动态链接库,放到lua源码src目录下,这样在lua中就可以调用base64接口,将输入字符串和输入字符串长度作为参数,返回输出字符串和输出字符串长度(以上base64源码写的很糙很SB,可以忽略base64的实现,主要看是如何将接口注册到lua中)。然后可以创建一个test.lua文件,输入:
local base64 = require "base64"
local input = "hello world!"
local res = base64.lbase64(input, string.len(input))
print(res)
运行文件,输出:aGVsbG8gd29ybGQh
2.将编写的函数接口与lua源码一起重新编译成lua解释器
源码:
// base64.cpp : 定义 DLL 应用程序的导出函数。
//
#include <stddef.h>
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
char code[65] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
'='
};
char* trans(const char* str, unsigned int num)
{
char* res = (char*)malloc(sizeof(char) * 4);
unsigned char* s = (unsigned char*)str;
*res = *s >> 2;
*(res + 1) = ((*(s + 1) >> 4) + (*(s) << 4)) & 0x3f;
*(res + 2) = ((*(s + 1) << 2) + (*(s + 2) >> 6)) & 0x3f;
*(res + 3) = *(s + 2) & 0x3f;
return res;
}
char transform(char s)
{
return code[s];
}
char* encode(const char* str, unsigned int num, unsigned int* resnum)
{
int numtmp = num;
int count = 0;
char* temp = NULL;
char* res = NULL;
char* point = NULL;
int i;
if((numtmp % 3) != 0)
numtmp = (numtmp / 3 + 1) * 3;
count = (numtmp / 3) * 4;
temp = (char*)malloc(numtmp * sizeof(char));
res = (char*)malloc(count * sizeof(char));
memset(temp, 0, numtmp * sizeof(char));
memset(res, 0, count * sizeof(char));
memcpy(temp, str, num);
for(i = 0; i < numtmp / 3; i++)
{
point = trans((temp + i * 3), 3);
memcpy((res+i*4), point, 4);
}
if((numtmp - num) == 1)
{
*(res + count - 1) = 64;
}
else if((numtmp - num) == 2)
{
*(res + count - 1) = 64;
*(res + count - 2) = 64;
}
else
{
//none
}
for(i = 0; i < count; i++)
{
*(res + i) = transform(*(res + i));
}
*resnum = count;
return res;
}
static int lbase64(lua_State* L)
{
const char* str = lua_tostring(L, 1);
unsigned int num = lua_tointeger(L, 2);
unsigned int count = 0;
char* res = encode(str, num, &count);
if(res)
{
lua_pushlstring(L, res, count);
lua_pushinteger(L, count);
return 2;
}
else
{
lua_pushnil(L);
return 1;
}
}
static const struct luaL_Reg base64[] = {
{"lbase64", lbase64},
{NULL, NULL}
};
LUA_API int luaopen_base64(lua_State* L)
{
luaL_register(L, "base64", base64);
return 1;
}
将写好的接口保存文件为lbase.c(此命名是有目的的,下面说)
在lua源文件linit.c中的luaL_openlibs中加入自己的luaopen_base64。
在lua源文件lualib.h中声明自己的luaopen_base64
这里可能看到上面的源码中luaopen_base64前有个LUA_API,并且在文件的开头有个宏定义#define LUA_LIB,这里在文件头部声明宏定义LUA_LIB表示这个模块是一个lua的函数库,因此下面的LUA_API表示是用来导出的。这里可以看下lua源文件luaconf.h文件找到关于这两个宏的解释。
最后使用与源码一同下载下来的编译脚本进行编译,编译脚本内容如下(脚本路径:lua-5.1.5\etc\luavs.bat):
@rem Script to build Lua under "Visual Studio .NET Command Prompt".
@rem Do not run from this directory; run it from the toplevel: etc\luavs.bat .
@rem It creates lua51.dll, lua51.lib, lua.exe, and luac.exe in src.
@rem (contributed by David Manura and Mike Pall)
@setlocal
@set MYCOMPILE=cl /nologo /MD /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE
@set MYLINK=link /nologo
@set MYMT=mt /nologo
cd src
%MYCOMPILE% /DLUA_BUILD_AS_DLL l*.c
del lua.obj luac.obj
%MYLINK% /DLL /out:lua51.dll l*.obj
if exist lua51.dll.manifest^
%MYMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2
%MYCOMPILE% /DLUA_BUILD_AS_DLL lua.c
%MYLINK% /out:lua.exe lua.obj lua51.lib
if exist lua.exe.manifest^
%MYMT% -manifest lua.exe.manifest -outputresource:lua.exe
%MYCOMPILE% l*.c print.c
del lua.obj linit.obj lbaselib.obj ldblib.obj liolib.obj lmathlib.obj^
loslib.obj ltablib.obj lstrlib.obj loadlib.obj
%MYLINK% /out:luac.exe *.obj
if exist luac.exe.manifest^
%MYMT% -manifest luac.exe.manifest -outputresource:luac.exe
del *.obj *.manifest
cd ..
脚本会创建lua.dll,lua.lia,lua解释器,lua编译器。
看到红色标记的地方(%MYCOMPILE% /DLUA_BUILD_AS_DLL l*.c),是编译src文件中所有l开头的文件,这也是上面说将写好的文件命名为lbase.c,这样就不需要改动脚本内容直接编译进去了。
然后可以创建一个test.lua文件,输入:
local base64= require "base64"
local input = "hello world!"
local res = base64.lbase64(input, string.len(input))
print(res)
运行文件,输出:aGVsbG8gd29ybGQh