为什么C在C ++不足的地方提供语言“绑定”?


60

我最近想知道何时在C ++上使用C,反之亦然?幸运的是,有人已经击败了我,尽管花了一段时间,但我能够消化该问题的所有答案和评论。

但是,该帖子中的一个项目一直不断地被处理,而没有任何示例,验证或解释:

“当您想为库提供多种语言绑定时,C代码非常有用”

这是一个释义。我应该指出,有几个人指出,C ++可以通过某种extern功能实现多种语言绑定,但是,如果您完整阅读该文章,则很明显C是可移植性/语言绑定的理想之选。我的问题是:为什么?

有人可以提供一些具体的原因,为什么用C编写库可以简化其他语言的绑定和/或可移植性?


6
主要是出于历史和社会原因。大多数语言实现和运行时系统都是在C之上构建的。例如,Ocaml,SBCL,Haskell运行时都使用C编码(通过C ++重新编码不会带来太大收益)
Basile Starynkevitch 2015年

8
在实践中,将真正的C ++库(考虑Qt)粘合到这些运行时是很痛苦的。
Basile Starynkevitch 2015年

3
@Basile:Qt不是真正的C ++库。而且,即使对于更痛苦的库,也只是痛苦,因为它们表达了实际上有用的语义。
DeadMG


2
阅读Don Box撰写的Essential COM。它说明了用C ++创建ABI的过程。COM是Microsoft创建ABI的方法。即可以从C或VB或其他语言使用COM C ++库。
user93353'5

Answers:


69

C具有非常简单得多的接口,并且其将源代码接口转换为二进制接口的规则非常简单,以至于生成外部接口以进行绑定都是以一种完善的方式完成的。另一方面,C ++具有非常复杂的接口,并且正式或实践中都没有对ABI绑定规则进行标准化。这意味着几乎任何针对任何平台的任何语言的编译器都可以绑定到外部C接口并确切知道期望什么,但是对于C ++接口,从根本上讲是不可能的,因为规则会根据哪个编译器,哪个版本以及哪个进行更改构建C ++代码的平台。

在C语言中,也没有标准的二进制语言实现规则,但这是一个简单的数量级,实际上,编译器使用相同的规则。使C ++代码难以调试的另一个原因是上述复杂的语法,因为调试器经常无法处理许多语言功能(在模板中放置断点,在数据显示窗口中解析指针转换命令,等等)。

缺乏标准的ABI(应用程序二进制接口)还有另一个后果-使得将C ++接口交付给其他团队/客户是不切实际的,因为除非使用相同的工具和构建选项进行编译,否则用户代码将无法正常工作。我们已经看到了此问题的另一个根源-由于缺少编译时封装而导致二进制接口的不稳定性。

- 有缺陷的C ++


4
您应该注意,尽管可以定义用C ++实现的类似C的API(extern "C",但这样做仍有更多工作要做,应该与C ++提供的功能相平衡)
2015年

5
C ++ ABI与问题上下文无关,在该问题中,可以使用轻松导出C ++库extern "C"
DeadMG

12
@jhominal:这比用C编写要好得多,在C中您必须定义C接口,然后还必须在C中实现它,而在C ++中,您可以定义C接口,然后就不必实现不管您使用哪种语言实现,都仍然必须定义一个C接口-这在C ++中当然是正确的,因为它在C语言或任何其他可以公开C绑定的语言中都是如此。
DeadMG

9
是否可以在该FQA驱动器的链接中添加免责声明?
马丁·巴

2
@DocBrown:在我看来,问题肯定是关于C语言绑定和C ++语言绑定的。
梅森·惠勒

32

如果你想用另一种语言的扬声器进行沟通,洋泾浜比莎士比亚的英语更容易。

C的概念-函数调用,指针,以NULL终止的字符串-非常简单,因此其他语言可以轻松地很好地实现它们以调用C函数。由于历史原因,许多其他语言都用C实现,这使得调用C函数更加容易。

C ++增加了很多东西-类,带有继承,vtables和访问修饰符;异常,随着堆栈的展开和控制流的改变;模板。所有这些使其他语言更难使用C ++绑定:充其量,要实现的是更多的“胶水”或互操作性代码,而最糟糕的是,这些概念不能直接转换(由于类模型,异常处理,等等。)。特别是对于模板,简单地使用(实例化)它们通常需要使用C ++编译器进行编译,这会使从其他环境使用它们变得非常复杂。

综上所述,有可能夸大了提供从C ++库到另一种语言的绑定的难度:

  • 如果您愿意使用C ++绑定,那么它可以与C兼容。正如@DeadMG指出的那样,C ++支持extern "C",因此您可以从C ++库中导出C样式语言绑定(具有C绑定的所有简单性和兼容性)(但有一个限制,即您不能公开任何C ++特定的功能) 。
  • 对C ++语言绑定的另一个常见反对意见是C ++缺乏ABI稳定性,但这也被夸大了。C ++ ABI的标准化程度低于C ABI,但是确实存在标准和事实上的标准(Itanium C ++ ABI,它也用于OS XGCC是 Linux 的事实上的标准)。Windows更糟,但是即使在Windows上,保持在一个Visual C ++版本中也应该可以正常工作。

1
提供从C ++库到另一种语言的绑定的另一个问题是,另一种语言可能要求使用C实现绑定。或者,对于具有(.NET)P / Invoke或(python)ctypes之类的语言,它可能不提供使用 C ++ ABI的任何工具。
Random832

6
@ Random832:当C ++端可以简单地提供C接口时,这是完全不相关的。您不必在C中实现绑定即可提供C绑定。
DeadMG

21

C是仍然存在的最古老的语言之一。它的ABI很简单,几乎所有今天仍在使用的操作系统都已写入其中。尽管其中一些操作系统可能已经在C#/。NET或其他任何顶级软件中添加了东西,但下面它们却深深浸入了C语言中。

这意味着,为了使用 OS提供的功能,实际上几乎每种编程语言都需要一种与C库进行交互的方法。Perl,Java,C ++ 本身都提供了“交谈C”的方法,因为如果不想重塑每个轮子,他们就不得不这样做。

这使C成为编程语言的拉丁语。(在隐喻必须是“编程语言的英语”之前,互联网已有多少年了?)


当用C编写库时,可以免费获得(显然)与C兼容的接口。如果您使用C ++编写库,则可以通过extern "C"上述声明获得C绑定。

但是,您只能可以在C中表达的功能获得这些绑定。

因此您的库API无法使用...

  • 模板,
  • 类,
  • 例外情况
  • 带有返回对象的任何函数。

一个简单的示例,您需要使导出的函数采用返回数组([])而不是std::vector(或者std::string就此而言)。

因此,您不仅无法提供C ++必须提供给库客户的任何好处,而且还必须付出额外的努力才能将库API从C ++“转换”为“ C兼容”(extern "C")。

这就是为什么可以指出C是实现库的更好选择的原因。就我个人而言,我认为C ++的好处仍然超过了extern "C"API 所需的努力,但这仅仅是我自己。


Windows似乎正在尝试以.NET为基础,Android基于Java(对于某些 API 的实现细节,也基于C ),而iOS / OSX基于Objective-C。
user253751

1
英语已经是编程世界中的主要语言。比其他行业更具统治力。
任思远

3
@immibis:Windows,Linux / Android和BSD / OSX都是用C编写的内核,其接口是用C编写的(以及为C编写的)。无论基于什么构建,Java都需要JNI,Perl则需要C调用,。 NET需要C调用,Python需要C调用,Objective-C需要C调用。这些都不需要 C ++调用,这就是我试图提出的重点。
2015年

@DevSolar许多Android的东西都是用Java本地编写的,并且不使用JNI(您可以“向后”使用它从C调用Java代码,但这可以进一步确认它是Java的)。没有使用iOS / OSX的经验,但是我听说与Objective-C相同。
user253751 2015年

3
@immibis:但是您确实知道OS内核与其用户空间之间的区别,不是吗?我严重怀疑,主要是Java用户空间使Android不能像台式机上运行的Linux内核那样成为具有C样式系统调用的Linux内核。我也怀疑他们在内核或中间件中使用Java。实际上,我知道他们在那里使用C。反之,这就是鸡蛋问题。它是用C完成之前,因此,它是如此容易得多还是做在C.
DevSolar

6

忽略其他已经提供的答案的详细信息:

如此多的语言提供C绑定的原因是,所有* nix和Windows操作系统都通过C接口公开了大多数OS API。因此,语言实现已经需要与C交互才能在主要的Oses上运行。因此,直接从语言本身提供与任何C接口的通信也很简单。


5

没有理由。如果您要表达的语义从根本上说是C兼容的,而不是模板,则没有理由使用C编写实现来简化绑定。实际上,从定义上说,C接口可以由可以满足二进制合同的任何实现(包括另一种语言的实现)填写。除了C ++,还有其他语言可以实现以这种方式工作的C二进制协定。

真正归结为那些不想学习新的语言或观念的人,他们迫切地试图选择任何理由留在恐龙时代,而这些语言或观念实际上具有有用的语义或功能。


4
我认为您是正确的,反对者误解了这个问题,但实际上您的答案没有强调突出潜在的误解:问题不在于“具有C接口的库”,而在于“具有C ++接口的库”,而是“完全用C编写的库”与“具有C编写的C接口的库”。
布朗

@Snowman:有问题的C ++绑定与公开C绑定的任何问题绝对无关。
DeadMG

2
因此,您将选择一种具有有用的语义和功能的语言,然后强迫自己将其转换为C接口?尽管类和模板是可以避免的(但首先要问为什么要使用C ++),但是具有C绑定的每个函数都必须捕获异常,而不是让它们转入C代码。更不用说使用C兼容库的任何人现在都必须处理C ++及其ABI冲突和库不匹配,以及底物C的ABI冲突和库不匹配。
prosfilaes,2015年

2
@prosfilaes:将异常转换为返回码很简单。您无需避免使用类,因为可以轻松地将类简化为C API,并且无需避免在实现中使用模板。而且,除非用户是从源代码构建的,否则您的用户无需担心C ++ ABI冲突,在这种情况下,无论如何,您都必须处理源语言。
DeadMG

4
我之所以投票,并不是因为不必一定要用C ++编写带有C API的库,而是因为除了恐龙之外,还有充分的理由使用C。如果您要使用X语言编写API,无论是C,FORTRAN,COBOL,BLISS还是Java,总有一段时间,用该语言编写然后变得更简单,更容易,更正确,更有趣的语言和界面两者。
prosfilaes,2015年

2

与另一种语言进行交互时,有两个主轴:

  • 接口可以继承的概念:仅仅是价值?参考?仿制药?
  • 如何在“二进制文件”(称为ABI)中实现接口

在这两个方面,C优于C ++:

  • C仅具有大多数简单的概念,几乎每隔一种语言就会出现1
  • C二进制文件的ABI由OS 2决定

现在,为什么大多数语言都具有与C类似的概念集,可能是由于它要么“简单”,要么是“预先存在”。没关系,关键是他们确实如此。

相反,C ++具有复杂的概念,并且ABI由每个编译器决定(尽管除了Windows以外,许多编译器都遵循Itanimum ABI)。实际上,Herb Sutter提出了一项建议,要求操作系统修复C ++ ABI(基于每个操作系统)以部分解决此问题。另外,应该注意,可以使用C ++ FFI,D正在尝试3

1除了variadics(...),这些都不简单

2 C是否有标准的ABI?

3将 D与旧版C ++代码接口


0

从根本上讲,它归结为ABI标准化。尽管C和C ++都没有其他语言可以用来在编写的二进制文件之间进行接口的标准化ABI,但C已成为事实上的标准,每个人都知道它,并且其他所有人都可以使用该语言所具有的相同,简单的规则类型和函数调用。

C ++可以有一个标准的ABI,但是Stroustrup说他认为不需要。他还表示,很难从编译器作者那里取得共识(尽管我怀疑-C ++标准委员会将发布与现有版本相似的ABI,并且编译器作者只会更改其编译器的下一个版本,这有时与二进制文件不兼容。仍然使用旧版本的编译器构建-我记得使用新的Sun编译器重新编译了几个库,发现它们无法与旧版本一起使用)

您会注意到,有些公司已经开始使用标准ABI,Microsoft早在90年代就开始使用COM进行此过程,而今天,他们将其改进为WinRT ABI(不要与其他WinRT ABI混淆)到表OS的一种类型),从而允许用C#编写的程序与用C或C ++编写的库进行通信(即Microsoft自己的OS层是用C ++编写的,使用WinRT公开并由C#应用程序在调用任何OS运行时例程时使用)

除非标准机构加强并解决这种情况,否则任何人都无能为力。微软显然看到了它的价值,并已采取措施对其平台进行解决。

因此,答案是C 确实不提供语言绑定。碰巧的是,没有人听过并且消费掉它们。


-2

所有答案都没有解决真正的问题:C ++编译引入了“名称修改”,因此二进制文件与“简单”函数调用不兼容。

ABI的所有内容仅是对其进行标准化的尝试。

通常,即使您坚持使用纯C ++,也不能保证可以交叉链接使用不同编译器编译的函数。以前可以肯定它们是不兼容的,但是如今标准化正在悄然兴起;)

OTOH C的精确设计是“高级组装”,并且允许各种简单的接口连接。它更适合跨语言喜好并不奇怪。

旁注:原始的C ++编译器(cfront)实际上产生了必须进行编译的C源代码,就像gcc在“幕后”产生了Assembly一样。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.