当前位置: 首页 > news >正文

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 <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#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);  //定义导出函数


luaclib.cpp


#include <stdio.h>
#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;
}

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

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

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

#include <iostream>
#include <lua.hpp>
/*相当于#include <lua.hpp>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
*/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;
}

hello.lua

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

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

Linux下

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

#include<stdio.h>
#include<lua.h>
#include<lauxlib.h>
#include<lualib.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}
};
//该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;
}

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!

需要把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<stdio.h>
#include<string.h>
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;
}
} 

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

编译好的动态库静态库资源
链接:https://pan.baidu.com/s/1hbLQm508bV-8PNgO6qiG1A?pwd=200v
提取码:200v
如遇任何问题,欢迎评论区留言~

相关文章:

  • springboot+jsp球队球员比赛数据管理系统java
  • upload-labs靶场通关指南(9-11关)
  • 【Arduino+ESP32专题】案例:使用INA3221监控电压电流
  • 微信小程序——语法篇
  • 【数据结构】交换排序之冒泡排序与快速排序
  • 第二十七章 使用后台任务页面
  • 【Hive】建表时的存储格式
  • 计算机网络 | 计算机网络体系结构
  • 【云原生】Docker的安装和卸载
  • 古琴【A5】良宵引
  • Causality
  • 【云原生 • Kubernetes】kubernetes 核心技术 - 持久化存储
  • 剑指offer--重建二叉树
  • 索引优化分析_预热_JOIN
  • 【Django】开发日报_1_Day:创建项目
  • 【面试系列】之二:关于js原型
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • Android系统模拟器绘制实现概述
  • Apache Spark Streaming 使用实例
  • Fundebug计费标准解释:事件数是如何定义的?
  • Java Agent 学习笔记
  • JavaScript设计模式系列一:工厂模式
  • LintCode 31. partitionArray 数组划分
  • Phpstorm怎样批量删除空行?
  • React 快速上手 - 07 前端路由 react-router
  • Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比...
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 使用 @font-face
  • 责任链模式的两种实现
  • 主流的CSS水平和垂直居中技术大全
  • Prometheus VS InfluxDB
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • #DBA杂记1
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (k8s中)docker netty OOM问题记录
  • (附源码)springboot教学评价 毕业设计 641310
  • (一)基于IDEA的JAVA基础10
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转载)CentOS查看系统信息|CentOS查看命令
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .NET Micro Framework初体验(二)
  • .net 无限分类
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • @RequestMapping处理请求异常
  • @transactional 方法执行完再commit_当@Transactional遇到@CacheEvict,你的代码是不是有bug!...
  • [04] Android逐帧动画(一)
  • [20161214]如何确定dbid.txt
  • [20171101]rman to destination.txt
  • [C puzzle book] types
  • [C# 基础知识系列]专题十六:Linq介绍
  • [caffe(二)]Python加载训练caffe模型并进行测试1
  • [FFmpeg学习]从视频中获取图片