1

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

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

构造函数调用的原型是:

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

但是我有: Failed to lookup symbol <constructor_function>,我尝试了构造函数:

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

我做到了nm -gDC <lib>,我可以看到构造函数。

帮助 !

编辑 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 实例的方法。

谢谢

4

2 回答 2

5

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 中,创建 typedef

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);
}
于 2020-05-04T16:55:58.140 回答
1

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);
}

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

于 2020-05-04T15:30:39.443 回答