使用tolua++实现C++与LUA相互调用

LUA是一种目前很流行的高效精简的脚本语言。LUA一个特点是比较方便的与C通讯。

然而要在脚本中使用C++类使用基本的LUA方法还是比较麻烦,纯手工暴露一个类的接口到LUA工作量还是很大的,而且都是一些简单的重复劳动。

好在有tolua++这个工具,可以让程序员从简单的重复劳动解脱出来。

tolua++包含两个部分,一个EXE,一个LIB,EXE用来通过package文件生成C函数的胶水函数,而LIB则用来为生成的胶水函数中用到的辅助函数提供实现。

 

在lua中要调用一个已有的C函数,大体上可以包含2步:

1、在C程序中实现一个胶水函数,该函数只有一个参数lua_State *。在这个胶水函数中,从lua的参数栈中逐个取出参数,再调用原有的C函数,最后将C函数的返回结果通过LUA的栈传回给LUA环境。

2、将该胶水函数注册到LUA的全局表中。

虽然这两个过程都很简单,但是当要暴露的是C++类大量的成员函数时,为每个函数编写胶水函数的工作可想而知。

有了tolua++,这个工作就很简单了,只需要做很少的工作就可以让tolua++自动为每一个待暴露的函数实现胶水函数。

如下面这样一个C++类,类的实现在一个命名空间TstNS中,类中包含有同一函数名的不同重载及自定义的结构体参数:

//file:export.h

#pragma once

#include <string>

using namespace std;

namespace TstNS

{

	struct RECT

	{

		int left, top, right, bottom;

	};

	class CExport

	{

	public:

		CExport(void);

		~CExport(void);



		int foo(int a, int b);

		int StrLen(const string & str);

		int StrLen(const char *pstr);

		int area(RECT rc);

		int area(RECT *prc);

		RECT setrect(int l, int t, int r, int b);

	};

}

为了使用tolua++导出这个类到lua中,我们需要写一个package文件,在该文件中定义哪里函数需要导出,我这里的实现如下:

//file:export.pkg



$#include "export.h"



namespace TstNS

{

	struct RECT

	{

		int left, top, right, bottom;

	};



	class CExport

	{

		CExport();

		~CExport();

		int foo(int a, int b);

		int StrLen(const string & str);

		int StrLen @ StrLen2(const char *pstr);

		int area(RECT rc);

		int area @ area2(RECT *prc); //将对area(void * prect)的调用重命名为area2

		RECT setrect(int l, int t, int r, int b);



	};

}

整 体上export.pkg中的内容基本与export.h文件类似,需要注意的是第一行:$#include "export.h"。tolua++根据这个文件来为其中的每个函数生成胶水函数于一个C文件中,由于胶水函数需要调用原来的函数,所以需要在生成的C 文件中需要包含export.h这个文件。第一行就是说在生成的C文件中插入一行#include “export.h"

第二个需要注意的地方在于函数的重载,LUA并不能根据参考类型自动选择调用哪一个重载函数,为此我们可以为不同的重载重命名。”@“正是为这一目的设计的。通过重命名,在LUA脚本中我们就可以显示的为不同的参数调用不同的重载函数。

我们需要使用tolua++的EXE来生成胶水函数代码。它是一个命令行程序,可以多个参数:

..\..\tolua++-1.0.93\bin\tolua++ -n export -o ..\lua_export.cpp export.pkg

这一行的意思是说调用tolua++.exe根据export.pkg生成一个..\lua_export.cpp的C++文件。-n export是说文件中一个需要被外面调用的C接口为:

int tolua_export_open (lua_State* tolua_S);

下一步,我们看一下在lua脚本中如何调用我们的导出的函数。

--file:test.lua

exp=TstNS.CExport:new();--首先我实例化一个CExport的全局对象。



function foo(a,b)

return exp:foo(a,b);

end



function StrLen(str)

return exp:StrLen(str);

end



function StrLen2(str)

return exp:StrLen2(str);

end



function area(rc)

return exp:area(rc);

end



function area2(prc)

--指针参数使用lua_call.hpp将以void*方式传入,需要做类型转换后才能调用C++中的代码。

return exp:area2(tolua.cast(prc,"TstNS::RECT"));

end



function setrect(l,t,r,b)

return exp:setrect(l,t,r,b);

end

在这个test.lua中我们实现了几个函数,在每一个函数中会调用CExport对应的接口。

到此lua调用C++的基本框架就已经实现了。

 

下面再看这个demo如何工作。

找到代码中的luatest目录,并打开luatest.sln这个VC工程,上面提到的代码在工程中都可以找到。

直接编译可以查看每一个函数是如何被调用的。

这里要推荐一个我改写的在C++中调用LUA函数的辅助模板类:lua_call.hpp

采用这个辅助类调用LUA函数就和调用普通的C函数一样简单。

下面是luatest.cpp中的代码:


 

// luatest.cpp : 定义控制台应用程序的入口点。

//



#include "stdafx.h"



#include "Export.h"

#include "tolua++.h"



extern int  tolua_export_open(lua_State* tolua_S);



#include "lua_call.hpp"//使用模板技术对C++调用lua函数进行包装,使用方法参见_tmain

//对于自定义的数据类型,需要特化push_value,及value_extractor两个接口。

template<>

void lua_function_base::push_value(TstNS::RECT rc)

{

	void* tolua_obj = Mtolua_new((TstNS::RECT)(rc));

	tolua_pushusertype(m_vm, tolua_obj, "TstNS::RECT");

	tolua_register_gc(m_vm, lua_gettop(m_vm));

}



template <>

TstNS::RECT lua_function_base::value_extractor()

{

	TstNS::RECT * val = (TstNS::RECT *) tolua_tousertype(m_vm, -1, 0);

	lua_pop(m_vm, 1);

	return *val;

}



int _tmain(int argc, _TCHAR* argv[])

{

	lua_State *L = lua_open();

	int nret = 0;

	luaL_openlibs(L);

	nret = tolua_export_open(L);

	nret = luaL_dofile(L, "lua/test.lua");



	{

		lua_function<int> lua_area(L, "area");

		TstNS::RECT rc = { 0,0,10,20 };

		int a1 = lua_area(rc);



		lua_function<int> lua_area2(L, "area2");

		TstNS::RECT rc2 = { 0,0,100,20 };

		int a2 = lua_area2(&rc2);



		lua_function<TstNS::RECT> lua_setarea(L, "setrect");

		rc = lua_setarea(5, 6, 7, 8);



		lua_function<int> lus_strlen(L, "StrLen");

		int nsize1 = lus_strlen(std::string("abcdefghij"));



		lua_function<int> lus_strlen2(L, "StrLen2");

		int nsize2 = lus_strlen("abcdefg");



		lua_function<int> foo(L, "foo");

		int sum = foo(5, 6);

	}



	lua_close(L);

	return 0;

}

 

在_main()中,首先当然是实例化lua环境。

调用tolua_export_open(L);注册tolua++实现的胶水函数到LUA。

调用luaL_dofile(L,"lua/test.lua");执行test.lua脚本。

 

调用lua脚本中实现的函数。

 

最后释放LUA环境:lua_close(L);

 

这其中lua脚本中实现的函数是比较取巧的。利用lua_call.hpp,我们只需要两行代码就可以实现:

第一步,定义一个lua_function对象,找到LUA中的函数。

第二步,为函数传入参数。

 

需要注意的是,当为CExport新增加一个需要导出的函数时,要记得将新的函数名加到export.pkg文件中,并调用tolua++重新生成胶水函数。在demo中有一个build.bat文件,每次更新后执行一次即可。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
tolua++是一个将C/C++代码导出到Lua的工具,它可以将C/C++代码转换成Lua解释器调用的API,并在运行时将C/C++代码封装成Lua可用的函数。 以下是一个简单的示例,演示如何使用tolua++C++代码转换成Lua调用的库: ```c++ // example.h class Example { public: Example(int value); int getValue(); private: int m_value; }; ``` ```c++ // example.cpp #include "example.h" Example::Example(int value) : m_value(value) { } int Example::getValue() { return m_value; } ``` 首先,我们需要使用tolua++生成绑定代码。假设我们的绑定代码文件名为examplebindings.pkg,其内容如下: ``` // examplebindings.pkg $#include "example.h" module example { class Example { public: Example(int value); int getValue(); }; } ``` 然后,我们可以使用tolua++命令行工具将绑定代码生成为C++头文件和Lua文件: ``` $ tolua++ -o examplebindings.h -H examplebindings.hpp examplebindings.pkg ``` 生成的examplebindings.h和examplebindings.hpp文件包含了C++的绑定代码,我们需要将其与原始的C++代码一起编译成库文件。 接下来,我们可以编写Lua脚本来调用C++代码: ```lua -- example.lua require "examplebindings" local example = example.Example(42) print(example:getValue()) -- 输出42 ``` 在上面的示例中,我们首先加载了examplebindings模块,然后创建了一个Example对象,并调用了其getValue()方法。注意,我们使用了冒号语法来调用方法,这是Lua中常用的一种语法。 最后,我们可以使用Lua解释器来运行example.lua脚本,以调用C++代码并输出结果。 以上就是使用tolua++C++代码转换成Lua调用的库的基本流程。需要注意的是,tolua++只支持部分C++特性,例如模板和多重继承等特性可能无法正确导出。在实际使用中,我们需要根据具体情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值