用于C++的脚本语言

41

我对脚本语言有点生疏了,虽然最近它们像蘑菇一样不断涌现 :)

今天我想要一个能够无缝连接C++的脚本语言,即可以使用C++类,并且最重要的是可以编译成C++或某些DLL/.SO(以及它的.h),这样我就可以将其链接到我的C++程序中,并利用脚本定义或实现的类。

我知道我可以嵌入任何流行的脚本语言,如lua、ruby、python... 但是接口通常包括某种“eval”函数来评估提供的脚本代码。根据用于将C++和脚本语言耦合的工具,将脚本回调集成到C++中的难易程度可能会更加容易或困难,但我还没有看到任何实际允许我编写独立模块并将其暴露为.h和.so/dll的脚本语言(也许沿着生成C++代码的脚本语言的思路)。

您知道有这样的工具/脚本语言吗?

谢谢您的帮助。

PS. 我一直在考虑像Vala或Haskell的GHC这样的语言,它们生成C,但不是C++...


4
这个问题在我看来似乎不是离题的。 - sergiol
抱歉,但是你为什么要使用脚本语言?除了用户输入之外,它还提供了什么其他功能吗? - Post Self
@DiegoSevilla,又诞生了一种新的编程语言。它被称为ZetScript。我不知道你是否正在寻找脚本引擎,但是请看一下,以防它符合你的需求! http://zetscript.org - Jordi Espada
10个回答

35
更新于2020:如果我真的想避免使用Lua作为语言,那么今天我可能会选择Lua+ Sol2/3。在这种情况下,Chaiscript成为一个不错的选择,尽管与Lua + Sol2/3 相比效率方面并不算最优(虽然它在多年的改进中有了很大的提高,所以在许多情况下仍然足够好)。 Falcon已经死亡了一些年,安息吧。
以下几个选项更加偏向于C++集成而非语言绑定:
  • ChaiScript - 在一个小项目中试用过,非常有趣,这是专门针对C++设计的,只需包含一个头文件即可使用!目前还不确定它是否适用于大型项目,但会进行尝试以获得一些体验!
  • (已不再维护) Falcon - 在一个大型项目中试用过,非常优秀;它不像ChaiScript那样是一个“单包含嵌入”,因为它真正灵活,并且完全是为了在C++中使用而设计的(仅在库中使用C++代码) - 我已经决定将其用于需要大量脚本灵活性的最大项目中 (可以与Ruby/Python相媲美)。
  • AngelScript - 尚未尝试
  • GameMonkey - 尚未尝试
  • Io - 尚未尝试
对于您而言,如果您真的想要在C++中编写脚本模块并轻松地将其暴露给脚本语言,我会推荐使用Falcon。它完全由C++制作,所有模块/库都是这样编写的。

谢谢,Klaim。这正是我在寻找的答案类型。我将尝试您的建议,然后我们看看。 - Diego Sevilla
1
@DiegoSevilla 嘿,你试过那些编程语言了吗?我回答你的问题正确吗? :) - Klaim
@DiegoSevilla 我想我会每两年再来这里,直到你验证我的答案 XD - Klaim
哈哈,很有趣,但我仍然认为它还没有被发明出来... :( ClojureC 接近了,ECL 和 Chicken Scheme 也朝着这个方向发展... 我会再次检查的... - Diego Sevilla
@DiegoSevilla,你的意思是ChaiScript不符合你的需求吗?还是Falcon也不行?(顺便说一下,Falcon正在进行大规模的新引擎发布,他们目前正在设计新的嵌入接口) - Klaim
显示剩余2条评论

21
在这种情况下通常会问的问题是:如何公开我的C++类,以便可以从脚本实例化它们?答案通常是像http://www.swig.org/这样的东西。
您提出了相反的问题,听起来您正在使事情变得更加复杂。生成.h文件和.so文件的脚本引擎实际上不会是脚本引擎-它将成为编译器!在这种情况下,您可以使用C ++。
脚本引擎不是这样工作的。您传递给它一个脚本和一些回调函数,这些回调函数提供了一组可以从脚本中调用的函数,然后引擎解释该脚本。

丹尼尔,是的,脚本引擎可以进行动态执行,但也可能不行。正如我所说,也许我正在寻找的是一种脚本语言,而不是一个脚本引擎。类似于GNOME的Vala,更像是编写快速代码的辅助工具,它会被转换为C++(在Vala的情况下是C),或者像ghc一样,实际上生成考虑到垃圾回收等因素的C代码。这将是一种针对C++的DSL,还具有可用的运行时库集合。也许谷歌的Go语言就是答案(奇怪的是没有人提到它)。 - Diego Sevilla
也许是因为 Go、Lua 和其他语言可以与 C 交互,但不能直接与 C++ 交互……来源:http://golang.org/doc/go_faq.html#Do_Go_programs_link_with_Cpp_programs - Klaim
谢谢,我之前完全不知道Swig的存在... - Susheel Javadi

21

尝试使用Lua:http://www.lua.org/

要在Lua中使用C++类,可以使用:

使用tolua++生成绑定:http://www.codenix.com/~tolua/

它以清理过的头文件作为输入,并输出一个执行艰苦工作的c文件。易于使用、美观、让人愉悦。

要在C++中使用Lua对象,我会采用编写通用代理对象的方法,其中包括(field, setField, callMethod, methods, fields)等方法。

如果您想要一个dll,在DllMain上使用.lua作为资源(在Windows上,我不知道Linux的适当等价物),并使用您的lua代码初始化您的代理对象。

然后,C++代码可以使用代理对象调用Lua代码,并在代理中添加一些内省方法,以使此任务更容易。

您可以重复使用代理对象来编写每个Lua库,只需更改提供给它的Lua代码即可。


链接已失效...... - Ran Wang
@RanWang 那是一个七年前的答案。我已经很久没用过tolua++了。在Google搜索“tolua++”会显示一些GitHub仓库 - 有没有其中一个适合你? - Vitor Py
找到了~ 非常感谢。 - Ran Wang

12

这略微超出了我的专业领域,但我愿意冒险遭受投票下降。 :-)

Boost::Python似乎是你正在寻找的东西。 它使用一些宏魔法来完成它的工作,但它相当干净地将Python类暴露给C ++。


我使用Boost:Python的经历并不愉快。代码似乎也在逐渐失去活力。相比之下,SWIG是一次更好的体验。 - Craig Wright

5
我是LikeMagic的作者,它是一个用于Io语言的C++绑定库。(我不是Io语言的作者。)

http://github.com/dennisferron/LikeMagic

我在LikeMagic中的一个明确目标是实现完全且双向的C++互操作性。 LikeMagic将本地Io类型作为C++类型进行编组(包括在Io中的STL容器和本机List类型之间进行转换),并在Io中表示C++类、方法、字段和数组。 您甚至可以传递一个Io代码块 Io环境,并将其用作C++中的函数对象!将C++类型封装以供Io脚本使用非常简单,快速且轻松。从C++访问脚本对象确实需要像您描述的“eval”函数,但是基于模板的类型转换和编组使得访问执行脚本字符串的结果变得容易。而且,上述能力可以将Io block()对象转换为C++函数对象。目前该项目仍处于早期阶段,尽管它已经完全可用。我仍然需要做一些事情,例如记录其构建步骤和依赖项,并且它只能使用gcc 4.4.1+(而不是Microsoft Visual C ++),因为它使用了MSVC尚未支持的C++0x功能。但是,它完全支持Linux和Windows,并计划进行Mac移植。

现在是个坏消息:让脚本生成.h文件和可从C++调用的.so或.dll文件不仅需要(某种形式的)编译器,而且还必须是即时编译器(JIT compiler)。这是因为(在许多脚本语言中,尤其是在Io中),对象的方法和字段直到运行时才会被知道,并且在Io中,方法甚至可以从活动对象中添加和删除!起初,我要说的是,你提出这个请求本身已经让我想到了你是否真正理解了动态语言的含义。但是,我相信一种设计方式,即首先尝试想象最理想或最容易的实现方式,然后从那里向后工作,直到达到实际可行的程度。因此,从易用性的角度来看,你所描述的内容听起来更加容易使用。

但是,虽然这是理想的,并且使用具有JIT编译的脚本语言刚好可以实现,但它并不是非常实用,因此我仍然不确定您所要求的是否是您真正想要的。如果.h和.so/.dll文件是从脚本中进行JIT编译的,并且脚本发生了更改,则需要重新编译C++程序以利用更改!这难道不违反了首先使用脚本的主要优点吗?
唯一实用的方法是,如果定义脚本的接口不会改变,您只需为脚本函数创建C++包装器。您最终会拥有许多类似于C++函数的函数:
int get_foo() { return script.eval("get_foo()"); }
int get_bar() { return script.eval("get_bar()"); }

我承认从调用包装函数的角度来看,这是更清晰的代码。但如果您想要这样做,为什么不在脚本语言中使用反射并根据存储在脚本对象中的方法列表生成一个.h文件呢?这种反射可以 在Io中轻松实现。我计划将OpenC++源到源代码转换器作为可调用库集成到LikeMagic中,这意味着您甚至可以使用强大的C++代码生成器而不是编写字符串。

2
您可以使用Lua来实现这一功能,但是如果您有很多类,则需要使用SWIG或tolua++等工具为您生成某些粘接代码。
这些工具都不能处理您问题的特殊部分,即在一个.h文件中隐藏一个脚本语言,并且让C ++代码调用脚本而不知道它们是脚本。为了实现这个目标,您需要执行以下步骤:
- 自己编写粘合代码。(对于Lua,这相对容易,直到涉及到类时,就变得不那么容易,这就是为什么存在像SWIG和tolua++这样的工具的原因。) - 在接口后面隐藏脚本解释器的某种全局状态。 - 假设您有多个.h文件,每个文件都使用脚本实现,您必须决定哪些文件在脚本语言中共享状态,哪些文件使用单独的脚本状态。(您基本上拥有脚本语言的VM,其中极端情况是(a)所有.h文件在公共的VM中使用相同的VM,(b)每个.h文件都有自己的单独、隔离的VM。其他选择更加复杂。)
如果您决定自己编写代码,将Lua表格转换为C++类的粘合代码(这样Lua代码看起来就像程序的其余部分使用的C++),相当简单。而将您的C++封装在Lua中(使得C++对象对于脚本来说就像Lua值一样)则比较麻烦。
无论您做什么,都需要进行一些工作。

1

谷歌的V8引擎是用C++编写的,我相信你可能能够将其集成到项目中。他们在这篇文章中谈到了如何做到这一点。


哦,不是嵌入式,而是脚本语言能够生成独立的.so模块(以及相应的.h文件可供从C++中使用)。 - Diego Sevilla
@Diego:嗯,我想你可以将一些JavaScript代码和V8解释器打包成一个“.so”文件 :-) 但我怀疑这不是你的意思。所以,需要编译JavaScript为机器码(而不是允许JavaScript与C++无缝通信),并且摆脱垃圾收集器(可能通过使用引用计数来实现)和解释器的其他方面。这是一个艰巨的任务。V8确实直接将JavaScript编译为机器码,但是在运行时动态地进行(因为JavaScript是一种动态语言)。从未听说过C++的这样一个编译器。[OT:Java有一个(Rhino)。] - T.J. Crowder

1

好问题,我经常自己思考这个问题,但是很遗憾这种事情没有简单的解决方案。如果你使用的是Windows(我猜不是),那么你可以通过在C++和VB中创建COM组件来实现类似的功能(将VB视为脚本语言)。对话通过COM接口进行,这是在不同语言之间进行交互的一种好方法。.NET基础语言也可以相互操作。

我也很想知道是否存在类似于C++的开源解决方案。


“which is a nice way to interop…”:我必须强烈反对。COM(特别是用于VB和C++接口)非常难以使用... - Alexandre C.
好的,是的,COM互操作有一些怪癖:),当在C++和VB之间使用时,可能更多地出现在.NET和COM之间; 你必须彻底理解并记住其中很多内容,这不是特别设计良好的东西。我自己不是COM的粉丝,几乎不能推荐它。 - tathagata


0
你可以考虑嵌入 Guile(一种Scheme解释器)或V8(Google的JavaScript解释器-用于Chrome-使用C ++编写)。

再次强调,我不想使用嵌入式。使用V8或Guile会迫使我编写特定的C++包装器。我希望脚本语言能够生成.h等文件。 - Diego Sevilla
我的错,没有仔细阅读你的问题。我不知道是否有任何语言工具可以轻松地做到你想要的。 - Ben Collins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接