Search code examples
c++flutterdartffidart-ffi

Can I call a C++ constructor function in dart ffi?


I'm new to ffi. But I successfully used dart-ffi with functions call.

Now, I'd like to use a C++ object in dart ffi. I don't know if it is possible, but I tried like this.

The prototypes of constructor call are :

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

But I've got : Failed to lookup symbol <constructor_function>, where I tried constructor function with :

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

I did nm -gDC <lib>, and I can see the constructor.

Help !

edit 1 : @Botje, @Richard-Heap

I'm trying to use the VideoCapture instance from OpenCV.

I have followed the instructions from Botje's answer.

So I created a lib, like this :

bind.hpp :

#ifndef BIND_HPP
# define BIND_HPP

#include <opencv2/videoio.hpp>

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

bind.cpp :

#include "bind.hpp"

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

The commands I use to compile :

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

I get : dart: symbol lookup error: ./lib/src/bind.so: undefined symbol: _ZN2cv12VideoCaptureC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEi

Next step, is to use a method of VideoCapture instance.

Thank you


Solution

  • C++ compilers use "name mangling" to ensure symbol names are unique. The fact that you had to add the -C option (or --demangle) to make it show up is a hint.

    For example, here is the mangled symbol for some_class::some_class(int, std::string):

    _ZN10some_classC2EiNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
    

    You will need to pass the mangled name (not the demangled name) in order to call the constructor. You will also need to match the ABI for an object (ie have a pointer to the object's memory in the correct register). Sometimes this is simply a hidden first argument to the constructor, but not in all ABIs.

    If at all possible, write a C++ wrapper function that constructs the object for you and tag it with extern "C" so you don't have to jump through these hoops.

    For example:

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

    You can now simply call create_some_class from Dart with basic types and you will get back a pointer to the constructed C++ object. If you intend to wrap a large API like this, consider migrating to something like SWIG that can auto-generate these wrappers.