将C/C++注册到LUA环境中使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nice_xp/article/details/52712140


刚刚学习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





猜你喜欢

转载自blog.csdn.net/nice_xp/article/details/52712140
今日推荐