将大型的C++库转换为C语言,以便在Swift或Go中导入。

huangapple go评论103阅读模式
英文:

Auto-Wrap huge C++ libs to C for import in Swift / Go

问题

假设我有一个庞大的C++库(有大量依赖项,在GCC下需要大约3小时进行完整构建)。我想在这个库的基础上进行开发,但不想使用C++,而是使用一种更高效的语言。我应该如何在另一种语言中桥接或封装这个外部库,以便可以在其上进行编程?

考虑的语言有:

  • Swift
  • Go

我发现这两种语言都提供了用于C库和代码的自动桥接或封装(我实际上不知道封装和桥接之间的区别)。所以,如果我有一些C代码,我可以将其放入同一个Swift或Go项目中,并在我的项目中使用简单的导入语句来使用它。

然而,对于C++代码,这种方法在这两种语言中都不适用。所以我搜索了如何将C++库转换为C代码或生成自动封装器的方法。我找到了以下内容:

  1. swig.org - C++库的自动封装器
  2. Comeau C++编译器 - 自动将C++转换为C代码
  3. LLVM - 应该能够将任何输入转换为LLVM能够处理的任何输出。

问题:

  1. 如果使用自动封装或桥接,是否有可能在其他语言(如Swift / Go)中构建在这样一个庞大的库之上?
  2. 在C++ -> C的过程中,列出的这3个库/程序/框架中哪个最适合?(因为Swift和Go都提供了C自动封装)
  3. 是否有比我目前考虑的更好的替代方案?
  4. 是继续使用C++更好,还是使用其他工具进行封装/桥接过程会带来太多工作量,无法弥补使用Swift / Go等更高效语言的好处?

谢谢:)

免责声明:手动将C++库封装为C的方法也是可行的,但对于这样一个庞大的库来说,这将需要难以承受的工作量。

英文:

Let's say i have a huge lib in C++ (with tons of dependencies, it needs about 3h for a full build under GCC). I want to build upon that lib but don't want to do so in C++ but rather in a more productive language. How can i actually bridge or wrap that extern lib package so i can access it in another language and program on top of it?

Languages considered:

  • Swift
  • Go

What i found is, that both languages do provide auto bridging or wrapping for C libs and code (I don't actually know whats the difference between wrapping / bridging). So, if i have some c code, i can just throw it in the same Swift or Go project and can use it with a simple import in my project.

This doesn't work in both languages for C++ code however. So i googled how to transform C++ libs to C code or generate autowrappers. I found the following:

  1. swig.org - auto wrapper for C++ libs
  2. Comeau C++ compiler - automatically transfers C++ to C code
  3. LLVM - should be able to take any input and transform it to any output that LLVM is capable of.

Question:

  1. Is it even in the realms of usable / realistic / managable to build
    on top of such a huge lib in other languages like Swift / Go, if
    using auto wrapping or auto bridging?
  2. What of the 3 listed libs / programs / frameworks works best for the process of C++ -> C (because Swift and Go both provide C auto
    wrapping).
  3. Are there better alternatives than what i considered so far?
  4. Would it be better to just "stick with C++" as using any other tools to do the wrapping / bridging process would be far to much
    work to equal out the benefit of using a more productive language
    like Swift / Go?

Thanks:)

Disclaimer: There is also the possibility to manually wrap a C++ lib in C but that would take an unbearable amount of work for such a huge lib.

答案1

得分: 2

Q1: 这是现实的吗?

不现实,因为任何复杂的C++互操作都会变得过于复杂。自动工具很可能会失败,而手动工作则太困难。

Q2: 什么是最好的选择?

我不知道,根据A1的情况,似乎并不重要。

Q3: 有其他选择吗?
Q4: C++是唯一的最佳选择吗?

如果你想利用来自其他语言的现有C++代码,而不考虑所涉及的语言,复杂情况下的最佳选择是使用混合方法。

大多数语言提供与C的互操作性,而不是C++,这是由于非标准的C++命名约定。换句话说,几乎每种语言都提供对纯C函数的访问,但往往不支持C++。

由于你的库很复杂,最好的解决方案是基于“Facade”模式。创建一个新的C库,并实现利用C++库的特定于应用程序的逻辑。尽量设计这个库尽可能薄。目标不是编写所有的业务逻辑,而是提供保持C++对象并调用C++函数的C函数。然后,GO级别的语言代码将调用此库以在底层使用C++库。这种方法与Q1的方法不同。在Q1中,你尝试每个C++函数或对象的方法进行一次互操作调用。在Facade中,你尝试实现对应用程序独特的C++使用场景。

通过Facade,你减少了互操作工作的范围,因为你针对的是应用程序场景。同时,你在GO语言级别上减少了对C++复杂性的依赖。

例如,你需要使用C++库读取温度传感器。

在C++中,你需要执行以下操作:

  1. 打开文件
  2. 读取流直到找到SLIP终止符
  3. 读取一个“记录”
  4. 关闭文件

使用Facade,你创建一个名为“readTemperature(deviceFileName)”的单个函数,该C函数一次执行4个调用。

这只是一个虚拟的例子,只是为了说明这一点。

使用Facade,你可能希望隐藏原始的C++对象,此时它变成了一个小的层次。这里的目标是保持专注,并在支持应用程序的泛化与应用程序需求之间取得平衡。

有趣的是,Facade方法是改善互操作性能的一种方式。几乎每种语言中的互操作性比普通操作更昂贵,因为需要从语言运行时环境中进行编组并保护它。大量的互操作调用会减慢应用程序的速度(我们在谈论百万级别)。例如,将10个互操作调用合并为1个可以提高性能,因为互操作操作的数量减少了。

英文:

Q1: Is it realistic?

Not realistic, because any large complicated C++ interop is going to get too complicated. Automatic tools are likely to fail and manual work is too hard.

Q2: What's best?

I don't know and given A1 it does not seem to matter.

Q3: Alternative?
Q4: Is C++ only the best alternative?

If you want to take advantage of existing C++ code from another language regardless of the language involved the best option in complex scenarios is to use a hybrid approach.

Most languages provide interop to C and not C++ due to non-standard C++ naming convention. In other words, just about every language provides access to plain C-functions, but C++ is frequently not supported.

Since your library is complex, the best solution would be based on "Facade" pattern. Create a new C-library and implement application specific logic that utilizes C++ library. Try to design this library to be as thin as possible. The goal is not to write all business logic, but to provide C-functions that hold on C++ objects and call C++ functions. The GO-level language code would then call this library to use C++ library underneath. This approach differs from Q1 approach. In Q1 you attempt to have one interop call on per C++ function or object's method. In Facade you attempt to implement C++ usage scenarios that are unique to your application.

With Facade you reduce the scope of interop work, because you target your application scenarios. At the same time you mitigate away from C++ complexity at GO language level.

For example, you need to read a temperature sensor using C++ library.

In C++ you'd have to do:

  1. open file
  2. read stream until you find SLIP terminator
  3. read one "record"
  4. close file

With facade you create a single function called "readTemperature(deviceFileName)" and that C function executes 4 calls at once.

That's a fake example, just to show the point.

With facade you might want to hide original C++ objects and at this point it becomes a small layer. The goal here is to stay focused and balance your application needs with generalization to support your application.

Interestingly enough Facade approach is a way to improve interop performance. Interop in just about every language is more expensive than normal operations due to need to marshal from langauage runtime environment and keep it protected. Lots of interop calls slow down application (we are talking about millions here). For example, having 10 interop calls combined into 1 improves performance, because amount of itnerop operations is reduced.

答案2

得分: 0

我成功地使用一个相对简单的过程,将一个大型的C++库(数百个头文件)包装成Swift。你只需要将你的项目直接链接到该库。你需要包装的只是你编写的任何新函数(在C++包装文件中调用的Swift函数),这些函数实际上使用了该库。冗长的代码可以留在包装文件中,大部分情况下不需要修改。这里有一个简单的教程对我很有帮助:https://www.swiftprogrammer.info/swift_call_cpp.html

(顺便说一下,他忽略了一个步骤:在构建设置中设置库搜索路径 => 搜索路径 => 库搜索路径(调试和发布都要设置))

英文:

I was successful wrapping a large (although perhaps not "huge") C++ library (hundreds of header files) in Swift using a relatively simple process. You directly link your project to the library. The only thing you have to wrap are any new functions that you write (to be invoked in Swift) that actually use the library (in the C++ wrapper file). The verbose stuff can be left in the wrapper file, mostly without any modification. There is a simple little tutorial which helped me: https://www.swiftprogrammer.info/swift_call_cpp.html

(FYI, there is one step he omitted: Set your library search paths in Build Settings => Search Paths => Library Search Paths (both Debug and Release) )

huangapple
  • 本文由 发表于 2016年8月23日 23:06:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/39104754.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定