• Lua脚本如何调用C/C++模块,Windows以及Linux版本演示


    Windows下

    我用的是vs2019,由于Windows下不像Linux可以直接直接安装lua程序直接运行lua代码,所以这里我们演示的是,通过c/c++调用lua脚本,lua脚本再调用其他的C/C++文件。

    先用vs2019创建一个windows桌面向导–控制台程序的工程
    在这里插入图片描述
    注意是选择windows桌面向导,项目名称必须是luaclib,到时候生成的dll文件为luaclib.dll,后边lua层用require调用c模块的时候dll文件的名称很重要。

    一班的动态库都需要.cpp和.h文件,但对于lua调用C/C++,不用.h文件也行, 这里我们为了正规一点,.cpp和.h一起用

    在开始之前,我们需要了解,lua5.1之后,lual_register()就被舍弃了,使用
    lua_newtable(L);
    luaL_setfuncs(L, myLib, 0);
    一起使用来进行替代。
    为了大家能够更深的理解,对于Windows下的lua调用C/C++模块,我们采用了lua5.1版本的方式,也就是使用lual_register()来进行测试;
    在Linux环境下,我们采用lua5.2版本(即使用lua_newtable(L);
    luaL_setfuncs(L, myLib, 0);来测试)

    首先是windows下

    luaclib.h

    #pragma once
    
     
    #include 
    #include 
    #include 
    
    #ifdef _LUA_EXPORTS  
    #define LUA_API __declspec(dllexport)
    #else
    #define lUA_API __declspec(dllimport)
    #endif
    
    
    //extern "C" LUA_API
     int luaopen_luaclib(lua_State * L);  //定义导出函数
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    luaclib.cpp

    
    #include 
    #include "luaclib.h"
    
    static int averageFunc(lua_State* L) {
    	int n = lua_gettop(L);
    	double sum = 0;
    	int i;
    
    	//循环参数求和
    	for (i = 1; i <= n; i++) {
    		sum += lua_tonumber(L, i);
    	}
    		lua_pushnumber(L, sum / n);	//压入平均值
    		lua_pushnumber(L, sum); // 压入和
    		return 2;	//返回两个结果
    }
    
    static int sayHelloFunc(lua_State* L) {
    	printf("hello world!");
    	return 0;
    }
    
    static const struct luaL_Reg myLib[] = {
    	{"average", averageFunc},
    	{"sayHello", sayHelloFunc},
    	{NULL, NULL}
    };
    
    int luaopen_luaclib(lua_State* L) {
    	//其函数名必须为luaopen_xxx,其中xxx表示library名称。Lua代码require "xxx"需要与之对应
    	//所以这里生成的dll必须是luaclib.dll
    	luaL_register(L, "ss", myLib); -- lua5.1版本及以前使用
    
    
    	/*lua_newtable(L);
    	luaL_setfuncs(L, myLib, 0);*/	--lua5.1之后的版本
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    将这个工程编译成.dll文件,可在创建工程的时候选择生成.dll文件,若忘记选择了,也可使用下图的方式设置后,将工程重新编译生成.dll文件。

    在这里插入图片描述不出意外得到的dll文件为luaclib.dll。
    至此动态库就准备好了,这个动态库就是我们后边要用lua来调用的C++模块。

    接下来重新创建一个工程,并且需要把上边得到的.dll 文件放到这个工程下。
    新建两个文件,一个test.cpp. 一个hello.lua
    test.cpp

    #include 
    #include 
    /*相当于#include 
    #include 
    #include 
    #include 
    */using namespace std;
    int main() {
        lua_State* L = luaL_newstate(); //lua_open();
    	luaL_openlibs(L);   //必不可少
        //加载lua文件
        int bRet = luaL_loadfile(L, "hello.lua");
        if (bRet) {
            cout << "load file error" << endl;
            return  0;
        }
        //运行lua文件
        bRet = lua_pcall(L, 0, 0, 0);
        if (bRet) {
            cout << "pcall error" << endl;
            return  0;
        }
        return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    hello.lua

    require "luaclib"  
    local avg,sum =ss.average(1,2,3,4,5)
    print(avg,sum)  -- 3 15  
    ss.sayHello()   -- hello world!
    
    • 1
    • 2
    • 3
    • 4

    到此为止,项目已经可以运行测试, 前提是配置好了lua环境,也就是一些头文件的引用,以及库文件的引用,如果你是新手,lua运行环境配置,去这篇文章配置好环境。

    Linux下

    Linux下就很简单了,因为可以在控制台上直接运行lua程序
    需要一个.c文件作为被调用的模块, 以及一个.lua文件
    test.c 其实和上边windows的test.c几乎一样,代码末端有区别。

    #include
    #include
    #include
    #include
    
    static int averageFunc(lua_State* L) {
    	int n = lua_gettop(L);
    	double sum = 0;
    	int i;
    
    	//循环参数求和
    	for (i = 1; i <= n; i++) {
    		sum += lua_tonumber(L, i);
    	}
    		lua_pushnumber(L, sum / n);	//压入平均值
    		lua_pushnumber(L, sum); // 压入和
    		return 2;	//返回两个结果
    }
    
    static int sayHelloFunc(lua_State* L) {
    	printf("hello world!");
    	return 0;
    }
    
    static const struct luaL_Reg myLib[] = {
    	{"average", averageFunc},
    	{"sayHello", sayHelloFunc},
    	{NULL, NULL}
    };
    //该C库的唯一入口函数。其函数签名等同于上面的注册函数。见如下几点说明:
    //1. 我们可以将该函数简单的理解为模块的工厂函数。
    //2. 其函数名必须为luaopen_xxx,其中xxx表示library名称。Lua代码require "xxx"需要与之对应。
    //3. 在luaL_setfuncs的调用中,其第二个参数为待注册函数的数组。
    //4. 需要强调的是,所有需要用到"xxx"的代码,不论C还是Lua,都必须保持一致,这是Lua的约定,
    //   否则将无法调用。
    int luaopen_luaclib(lua_State* L)
    {
    	//const char* libName = "mytestlib"; //
    	//luaL_register(L, libName, mylib); //由于在lua-5.2中已没有luaL_register这个函数,所以换成下面两行代码
    	
    	lua_newtable(L);
    	luaL_setfuncs(L, myLib, 0);
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    test.lua

    #!/usr/local/bin/lua	//有了这行命令,可以直接 ./test.lua 运行lua程序
    local mylib = require("luaclib")  --对应于teste.c中的包名      
    local avg,sum =mylib.average(1,2,3,4,5)
    print(avg,sum)  -- 3 15  
    mylib.sayHello()   -- hello world!
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要把test.c文件编译成一个动态库
    gcc -o luaclib.so -fPIC -shared test.c

    1. -shared,众所周知啊,.so时share object的缩写。为了让gcc知道是在编译 .so而不是可执行文件,所以需要-shared

    2. -fPIC,这是编译动态库必须的。可见黄色圈起来的地方,分开编译,没加-fPIC,就会报错,提醒重新编译。其实-fPIC是编译选项,PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性。

    这里必须是gcc, 不能用g++, g++是用在cpp文件中的。
    建议你先不要用cpp文件,和我一样,先用c文件测试成功后再尝试cpp,因为cpp涉及了extern ”c”的使用,不然函数名字导出成动态库的时候会出错。

    使用命令, lua test.lua 即可看到效果。
    如果lua文件中添加了#!/usr/local/bin/lua, 可直接 ./test.lua

    上边这种是调用的c模块,如果是c++模块有一点小区别, 必须要使用extern"C", 它的作用是告诉系统以c语言的方式来导出动态库文件,我们都知道c++有函数重载这个说法,所以,在c++中的动态库的函数名字与c语言中是不一样的,因为c++中可能出现同名函数,所以c++在导出的动态库中对函数名进行了一些额外的标志,所以我们需要用到extern“C”来以C语言的方式导出

    只需把上边的test.c 改为 test.cpp
    test.cpp

    #include
    #include
    extern "C" {  
    #include "lua.h"  
    #include "lualib.h"  
    #include "lauxlib.h"  
    } 
    extern "C" {  
    static int averageFunc(lua_State* L) {
    	int n = lua_gettop(L);
    	double sum = 0;
    	int i;
    	//循环参数求和
    	for (i = 1; i <= n; i++) {
    		sum += lua_tonumber(L, i);
    	}
    		lua_pushnumber(L, sum / n);	//压入平均值
    		lua_pushnumber(L, sum); // 压入和
    		return 2;	//返回两个结果
    }
    static int sayHelloFunc(lua_State* L) {
    	printf("hello world!");
    	return 0;
    }
    static const struct luaL_Reg myLib[] = {
    	{"average", averageFunc},
    	{"sayHello", sayHelloFunc},
    	{NULL, NULL}
    };
    int luaopen_mytestlib(lua_State* L)
    {
    	lua_newtable(L);
    	luaL_setfuncs(L, myLib, 0);
    	return 1;
    }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    区别就是加了extern“C“ ,然后将命令改成
    g++ -o luaclib.so -fPIC -shared test.c (上边用的是gcc)

    编译好的动态库静态库资源
    链接:https://pan.baidu.com/s/1hbLQm508bV-8PNgO6qiG1A?pwd=200v
    提取码:200v

    这里只是lua调用C/C++,去这一篇学习 C/C++ 如何调用lua(点我)
    如遇任何问题,欢迎评论区留言~

  • 相关阅读:
    51单片机学习:LED点阵实验(点亮一个点)
    Volt Active Data介绍
    BRAM/URAM资源介绍
    milvus集合管理
    JavaSE | 顺序表练习
    向Visual Studio Code导入ST项目
    使用libcurl实现Amazon网页抓取
    为域名加https的方法是什么?
    性能测试计划怎么写?
    【MindSpore】【SoftmaxCrossEntropyWithLogits】如何计算带有class_weight的交叉熵
  • 原文地址:https://blog.csdn.net/qq_51721904/article/details/126789547