【问题标题】:Can I call a C++ constructor function in dart ffi?我可以在 dart ffi 中调用 C++ 构造函数吗?
【发布时间】:2020-05-04 14:55:48
【问题描述】:

我是 ffi 的新手。但我成功地将 dart-ffi 与函数调用一起使用。

现在,我想在 dart ffi 中使用 C++ 对象。我不知道是否可能,但我尝试过这样。

构造函数调用的原型是:

function_dart = lib
    .lookup<NativeFunction<function_native>>("constructor_function")
    .asFunction();

但我有: Failed to lookup symbol &lt;constructor_function&gt;,在那里我尝试了构造函数:

constructor_function
class::constructor_function
class::constructor_function(args)

我做了nm -gDC &lt;lib&gt;,我可以看到构造函数。

帮助!

编辑 1: @Botje,@Richard-Heap

我正在尝试使用 OpenCV 中的 VideoCapture 实例。

我已按照 Botje 的回答中的说明进行操作。

所以我创建了一个库,如下所示:

绑定.hpp:

#ifndef BIND_HPP
# define BIND_HPP

#include <opencv2/videoio.hpp>

extern "C" {
  cv::VideoCapture *cvCreateVideoCapture(char *filename, int apiPreference);
}
#endif

绑定.cpp:

#include "bind.hpp"

cv::VideoCapture *createVideoCapture(char *filename, int apiPreference) {
  return new cv::VideoCapture(filename, apiPreference);
}

我用来编译的命令:

g++ -c bind.cpp -lopencv -o bind.o
g++ bind.o -shared -o bind.so

我明白了:dart: symbol lookup error: ./lib/src/bind.so: undefined symbol: _ZN2cv12VideoCaptureC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEi

下一步,是使用 VideoCapture 实例的方法。

谢谢

【问题讨论】:

  • C++ 没有程序员“调用构造函数”的概念(由于某些声明和强制转换语法的外观,我意识到有些人认为它确实如此),并且构造函数没有名称。对象是由更复杂的机器创建的,其中大部分都隐藏在语言的语义中,不能直接提供给您。你想完成什么?
  • 我正在尝试将第 3 方库与 C++ 对象一起使用,但我不想重写它。更准确地说,我必须仅使用 C++ 对象构造函数打开文件格式。我可以编写一个接口,但不能自己重写对象。这似乎是一个矫枉过正的工作,只是为了可能性,我可能不知道。
  • -lopencv 应该继续链接步骤 (gcc ... -o bind.so),而不是编译步骤。如果还有其他问题,请打开一个新问题,而不是污染这个问题。

标签: c++ flutter dart ffi dart-ffi


【解决方案1】:

Dart ffi 使用的是 C 接口,所以你要适应如下。

从 C++ 类开始

Rect::Rect(int32_t width, int32_t height) {
  m_width = width;
  m_height = height;
}

void Rect::setWidth(int32_t width) {
  m_width = width;
}

void Rect::setHeight(int32_t height) {
  m_height = height;
}

int32_t Rect::area() {
  return m_width * m_height;
}

创建一个 C 适配器头

EXTERNC void* rect_init(int32_t width, int32_t height);
EXTERNC void rect_destroy(void *ptr);
EXTERNC int32_t rect_area(void *ptr);

和实施

void* rect_init(int32_t width, int32_t height){
    return new Rect(width, height);
}

void rect_destroy(void *ptr){
    auto typed_ptr = static_cast<Rect*>(ptr);
    delete typed_ptr;
}

int32_t rect_area(void *ptr){
    auto typed_ptr = static_cast<Rect*>(ptr);
    return typed_ptr->area();
}

在 Dart 中,创建 typedefs

typedef example_init_rect = Pointer<Void> Function(Int32 w, Int32 h);
typedef ExampleInitRect = Pointer<Void> Function(int w, int h);

typedef example_free_rect = Void Function(Pointer<Void> p);
typedef ExampleFreeRect = void Function(Pointer<Void> p);

typedef example_area_rect = Int32 Function(Pointer<Void> p);
typedef ExampleAreaRect = int Function(Pointer<Void> p);

并绑定 C 适配器函数。最后,您可以创建一个代理底层 C++ 类的 Dart 类。

class NativeRect {
  Pointer<Void> _nativeInstance;

  NativeRect(int width, int height) {
    _nativeInstance = Example()._exInitRect(width, height);
  }

  void free() {
    Example()._exFreeRect(_nativeInstance);
  }

  int get area => Example()._exAreaRect(_nativeInstance);
}

【讨论】:

  • 这对我来说很清楚,但只是为了确定。可以不用第一步,我用的是第三方库,想避免重写吗?
  • @XavierB 是的,如果你已经上过这门课,就没有必要再做一个了。
  • 这是一个很好的答案。包装/编组的教科书示例。
  • @xavierb 是的,你可以像这样包装一个现有的类,但你仍然需要用 C 或 C++ 编写瘦适配器来导出函数。如果您的适配器位于 C++ 文件中,请使用 extern "C"。
  • 有没有办法生成这个(对于一个大图书馆),还是我需要手动完成?
【解决方案2】:

C++ 编译器使用“名称修饰”来确保符号名称是唯一的。您必须添加 -C 选项(或 --demangle)才能使其显示这一事实是一个提示。

例如,这里是some_class::some_class(int, std::string) 的错位符号:

_ZN10some_classC2EiNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE

您需要传递经过修饰的名称(而不是经过修饰的名称)才能调用构造函数。您需要匹配对象的ABI(即在正确的寄存器中有指向对象内存的指针)。有时这只是构造函数的隐藏第一个参数,但并非在所有 ABI 中。

如果可能,请编写一个 C++ 包装函数来为您构造对象并用 extern "C" 标记它,这样您就不必跳过这些环节。

例如:

extern "C"
some_class* create_some_class(int x, const char * some_text) {
    return new some_class(x, some_text);
}

您现在可以使用基本类型从 Dart 中简单地调用 create_some_class,您将返回一个指向已构造 C++ 对象的指针。 如果您打算像这样包装大型 API,请考虑迁移到可以自动生成这些包装器的 SWIG 之类的东西。

【讨论】:

  • 好的,有 2 个问题:我遵循了您的解决方案,我觉得它非常好,我的库使用了我需要的 3rd 方库。但我有undefined &lt;mangled_constructor&gt; from libofmine.so。该方法在 libofmine 中很好(使用 nm -gDC 看到),但为“U”/未定义。 1.我该如何解决这个问题? 2.如果一切顺利,我该如何调用实例的方法?谢谢
  • 您能否编辑您的问题以显示libofmine.so 是如何创建的以及哪个库包含构造函数?至于你的第二个问题:更多的 C 包装器代码或者可能以对象作为第一个参数调用适当的重整函数名称。
  • 好的,我用更多细节编辑了我的问题(openCV,我要包装的代码)。感谢您的帮助。
猜你喜欢
  • 2014-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 2018-01-14
相关资源
最近更新 更多