免费教程_免费网赚教程_破解版软件-寂涯网络学习基地

当前位置: 主页 > 系统综合 > 各类编程 > Lua与C/C++交互——C/C++导出Dll

Lua与C/C++交互——C/C++导出Dll

时间:2011-10-18 23:09来源: 整理:寂涯网络 点击:

 

0.简介

         Lua(念“鲁啊”)作为一门发展成熟的脚本语言,正在变得越来越流行。它也可以作为和C/C++执行脚本交互的语言。并且Lua的整个库很小,我安装了最新的正式版Lua 5.1版本,而整个静态链接的lua.dll才164KB,所以Lua很轻量,特别适合轻量级脚本嵌入。

 

        看文章名就知道,我们要讲Lua和C/C++的交互,这期讲Lua如何使用C/C++的东西——Lua通过C/C++导出的dll来调用。OK,Let’s Go!首先,你得去Lua官网http://www.lua.org获取最新版本的Lua。

 

1.准备工作

       安装完Lua,需要在Visual Studio中配置Lua路径,使得你的编译器能搜寻到。关于VS2010的配置,见我的博文《VS2010 C++目录配置》一文。完成后新建一个Dll工程便可以了。

 

       我们用一个在Lua中显示Windows对话框的程序来简要介绍一下,程序虽小,但五脏俱全。程序如下:

 

// 

// 将一些有用的Win32特性导出. 

// 以便在Lua中使用. 

// 

 

 

extern "C" 

#include <lua.h> 

#include <lualib.h> 

#include <lauxlib.h> 

#pragma comment(lib, "lua.lib") 

}; 

 

 

#include <Windows.h> 

#include <iostream> 

using namespace std; 

 

 

static const char* const ERROR_ARGUMENT_COUNT = "参数数目错误!"; 

static const char* const ERROR_ARGUMENT_TYPE  = "参数类型错误!"; 

 

 

// 

// 发生错误,报告错误. 

// 

void ErrorMsg(lua_State* luaEnv, const char* const pszErrorInfo) 

    lua_pushstring(luaEnv, pszErrorInfo); 

    lua_error(luaEnv); 

 

 

// 

// 检测函数调用参数个数是否正常. 

// 

void CheckParamCount(lua_State* luaEnv, int paramCount) 

    // lua_gettop获取栈中元素个数. 

    if (lua_gettop(luaEnv) != paramCount) 

    { 

        ErrorMsg(luaEnv, ERROR_ARGUMENT_COUNT); 

    } 

 

 

// 

// 显示Windows对话框. 

// @param [in] pszMessage string 1 

// @param [in] pszCaption string 2 

// 

extern "C" int ShowMsgBox(lua_State* luaEnv) 

    const char* pszMessage = 0; 

    const char* pszCaption = 0; 

 

    // 检测参数个数是否正确. 

    CheckParamCount(luaEnv, 2); 

 

    // 提取参数. 

    pszMessage = luaL_checkstring(luaEnv, 1); 

    pszCaption = luaL_checkstring(luaEnv, 2); 

 

    if (pszCaption && pszMessage) 

    { 

        ::MessageBox( 

            NULL, 

            pszMessage, 

            pszCaption, 

            MB_OK | MB_ICONINFORMATION 

            ); 

    } 

    else 

    { 

        ErrorMsg(luaEnv, ERROR_ARGUMENT_TYPE); 

    } 

 

    // 返回值个数为0个. 

    return 0; 

 

 

// 

// 导出函数列表. 

// 

static luaL_Reg luaLibs[] = 

    {"ShowMsgBox", ShowMsgBox}, 

    {NULL, NULL} 

}; 

 

 

// 

// Dll入口函数,Lua调用此Dll的入口函数. 

// 

extern "C" __declspec(dllexport) 

int luaopen_WinFeature(lua_State* luaEnv) 

    const char* const LIBRARY_NAME = "WinFeature"; 

    luaL_register(luaEnv, LIBRARY_NAME, luaLibs); 

 

    return 1; 

 

 

//:-) 

2.程序解析

        首先我们包含Lua的头文件,并链入库文件。注意:Lua的头文件为C风格,所以用external “C”来含入。在此例中,我们最终的导出函数为“ShowMsgBox”。

 

        每一个导出函数的格式都为:

 

extern “C”int Export_Proc_Name(luaState* luaEnv); 

其中,luaState*所指的结构中包含了Lua调用此Dll时必备的Lua环境。那么Lua向函数传递参数该怎么办呢?实际上是用luaL_check[type]函数来完成的。如下:

 

const char* pHelloStr = luaL_checkstring(luaEnv, 1); 

 

double value = luaL_checknumber(luaEnv, 2); 

int ivalue = luaL_checkint(luaEnv, 3); 

luaL_check系列函数的第二个参数是Lua调用该函数时传递参数从坐到右的顺序(从1开始)。

 

        然后我们看到,static的一个luaL_Reg的结构数组中包含了所有要导出的函数列表。最后通过luaopen_YourDllName的一个导出函数来完成一系列操作。YourDllName就是你最终的Dll的名字(不含扩展名)。因为你在Lua中调用此Dll时,Lua会根据此Dll名字找luaopen_YourDllName对应的函数,然后从此函数加载该Dll。

 

Dll入口函数格式如下:

 

extern "C" __declspec(dllexport) 

int luaopen_WinFeature(lua_State* luaEnv) 

    const char* const LIBRARY_NAME = "WinFeature"; 

    luaL_register(luaEnv, LIBRARY_NAME, luaLibs); 

 

    return 1; 

我们通过luaL_register将LIBRARY_NAME对应的库名,以及luaL_Reg数组对应的导出列表来注册到lua_State*对应的Lua环境中。

 

3.Lua调用

        那么我们要如何调用该Dll呢?首先,把该Dll放到你的Lua能搜寻到的目录——当前目录、Lua安装目录下的clibs目录……然后通过require函数导入。

 

        因为Lua中如果你的函数调用参数只有一个,并且该参数为字符串的话,函数调用时的括号是可以省略的,所以:

require(“YourLibName”)和requir“YourLibName”都是合法的。我们把刚刚生成的WinFeature.dll文件拷贝到C盘下,然后在C盘启动Lua。示例如下:

\

 

可以看到,函数调用方式都是“包名.函数名”,而包名就是你的Dll的名字。我们可以用下面的方式查看一个包中的所有函数:

 

 

for k, v in pairs(PackageName) do 

    print(k, v) 

end 

然后我们调用WinFeature.ShowMsgBox函数:

 

\

4.Lua堆栈详解

 

       唔,那么lua_State结构如何管理Lua运行环境的呢?Lua又是如何将参数传递到C/C++函数的呢?C/C++函数又如何返回值给Lua呢?……这一切,都得从Lua堆栈讲起。

 

       Lua在和C/C++交互时,Lua运行环境维护着一份堆栈——不是传统意义上的堆栈,而是Lua模拟出来的。Lua与C/C++的数据传递都通过这份堆栈来完成,这份堆栈的代表就是lua_State*所指的那个结构。

 

4.1.堆栈结构解析

       堆栈通过lua_push系列函数向堆栈中压入值,通过luaL_check系列从堆栈中获取值。而用luaL_check系列函数时传递的参数索引,比如我们调用WinFeature.ShowMsgBox(“Hello”, “Tip”)函数时,栈结构如下:

\       

其中,参数在栈中的索引为参数从左到右的索引(从1开始),栈顶元素索引也可以从-1记起。栈中元素个数可以用lua_gettop来获得,如果lua_gettop返回0,表示此栈为空。(lua_gettop这个函数名取得不怎么样!呵呵)

 

 

4.2.提取参数

         luaL_check系列函数在获取值的同时,检测这个值是不是符合我们所期望的类型,如果不是,则抛出异常。所有这个系列函数如下:

 

luaL_checkany          ——检测任何值(可以为nil) 

luaL_checkint          ——检测一个值是否为number(double),并转换成int 

luaL_checkinteger      ——检测一个值是否为number(double),并转换成lua_Integer(prtdiff_t),在我的机子上,ptrdiff_t被定义为int 

luaL_checklong         ——检测一个值是否为number(double),并转换成long 

luaL_checklstring      ——检测一个值是否为string,并将字符串长度传递在[out]参数中返回 

luaL_checknumber       ——检测一个值是否为number(double) 

luaL_checkstring       ——检测一个值是否为string并返回 

luaL_checkudata        ——检测自定义类型 

 

4.3.传递返回值

        当我们要传递返回值给Lua时,可以用lua_push系列函数来完成。每一个导出函数都要返回一个int型整数,这个整数是你的导出函数的返回值的个数。而返回值通过lua_push系列函数压入栈中。比如一个Add函数:

 

extern “C” int Add(lua_State* luaEnv) 

    CheckParamCount(luaEnv, 2); 

 

    double left = luaL_checknumber(luaEnv, 1); 

    double right = luaL_checknumber(luaEnv, 2); 

 

    double result = left + right; 

    lua_pushnumber(luaEnv, result); 

     

    return 1; 

可以看出,我们用lua_pushnumber把返回值压入栈,最后返回1——1代表返回值的个数。lua_push系列函数如下:

 

lua_pushboolean        ——压入一个bool值 

lua_pushcfunction      ——压入一个lua_CFunction类型的C函数指针 

lua_pushfstring        ——格式化一个string并返回,类似于sprintf 

lua_pushinteger        ——压入一个int 

lua_pushlightuserdata  ——压入自定义数据类型 

lua_pushliteral        ——压入一个字面值字符串 

lua_pushlstring        ——压入一个规定长度内的string 

lua_pushnil            ——压入nil值 

lua_pushnumber         ——压入lua_Number(double)值 

lua_pushstring         ——压入一个string 

lua_pushthread         ——压入一个所传递lua_State所对应的线程,如果此线程是主线程,则返回1 

lua_pushvalue          ——将所传递索引处的值复制一份压入栈顶 

lua_pushvfstring       ——类似lua_pushfstring 

通过这些函数,我们可以灵活的使用C/C++的高性能特性,来导出函数供Lua调用

摘自:Arnozhang的专栏

本页地址 http://www.jybase.net/biancheng/20111018113.html

百度搜索更多

谷歌搜索更多

顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------

评价:
昵称: 验证码:点击我更换图片
推荐内容
赞助商
赞助商


关于本站免责声明视频更新google百度地图视频地图RRS订阅

如有什么问题请在本站留言,或发邮件到 hxt167#foxmail.com