Issue calling static C++ library from Flutter C-FFI

507 views
Skip to first unread message

Jamie Collinson

unread,
Feb 14, 2021, 8:48:10 AM2/14/21
to Flutter Development (flutter-dev)
Hi, I'm trying to get FFI working to interface with a C++ library (https://support.bayesfusion.com/docs/SMILE/) I'd like to use in a Flutter app. The library is distributed as a set of .h headers and a static libsmile.a file containing the proprietary engine. 

I've successfully been through the Flutter C-FFI tutorial, and have followed up by writing an example C++ function in a file named smile.cpp which calls the library. I've tested this separately by compiling with g++ and it works correctly.

When I try to incorporate into the build process indicated by the Flutter C-FFI tutorial I hit a problem - certain symbols are undefined and the linker is suggesting mangled names from the static library, i.e. in the below DSL_network <-> __ZN11DSL_networkC1Ev:

> Build command failed.
Error while executing process /Users/jamie/Library/Android/sdk/cmake/3.6.4111459/bin/cmake with arguments {--build /Users/jamie/work/flutter_app/native_add/android/.cxx/cmake/debug/armeabi-v7a --target smile_test}
[1/1] Linking CXX shared library /Users/jamie/work/flutter_app/native_add/example/build/native_add/intermediates/cmake/debug/obj/armeabi-v7a/libsmile_test.so
FAILED: : && /Users/jamie/Library/Android/sdk/ndk/22.0.7026061/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ --target=armv7-none-linux-androideabi16 --gcc-toolchain=/Users/jamie/Library/Android/sdk/ndk/22.0.7026061/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=/Users/jamie/Library/Android/sdk/ndk/22.0.7026061/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security -O0 -fno-limit-debug-info -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,--fatal-warnings -Wl,--exclude-libs,libunwind.a -Wl,--no-undefined -Qunused-arguments -shared -Wl,-soname,libsmile_test.so -o /Users/jamie/work/flutter_app/native_add/example/build/native_add/intermediates/cmake/debug/obj/armeabi-v7a/libsmile_test.so CMakeFiles/smile_test.dir/Users/jamie/work/flutter_app/native_add/ios/Classes/smile.cpp.o -L/Users/jamie/work/flutter_app/native_add/android/../ios/lib/smile-1.6.0-B -lsmile -latomic -lm && :
ld: error: undefined symbol: DSL_network::DSL_network()
>>> referenced by smile.cpp:11 (/Users/jamie/work/flutter_app/native_add/ios/Classes/smile.cpp:11)
>>> CMakeFiles/smile_test.dir/Users/jamie/work/flutter_app/native_add/ios/Classes/smile.cpp.o:(test_network_load)
>>> did you mean: __ZN11DSL_networkC1Ev
>>> defined in: /Users/jamie/work/flutter_app/native_add/android/../ios/lib/smile-1.6.0-B/libsmile.a

I'm not very familiar with C/C++ interop but I think this is probably related to name mangling? 

Can anyone point me in the right direction to resolve?I'm hoping I'm missing something obvious from my CMakeLists.txt which is currently:

cmake_minimum_required(VERSION 3.4.1)
add_definitions(-DNDEBUG)set(SMILE_PATH ../ios/lib/smile-1.6.0-B)

include_directories(${SMILE_PATH})
link_directories(${SMILE_PATH})

add_library(smile_test SHARED ../ios/Classes/smile.cpp)
target_link_libraries(smile_test smile)

Brett Morgan

unread,
Feb 14, 2021, 11:42:55 PM2/14/21
to Jamie Collinson, Flutter Development (flutter-dev)
A key part of this puzzle is that C++ doesn't have an ABI. See this pdf for more information.

The usual way around this is to expose a C API from the C++ code, and as C does actually have an ABI, you can target this exported C API from Dart's FFI.

--
You received this message because you are subscribed to the Google Groups "Flutter Development (flutter-dev)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/flutter-dev/f1e40e4c-986d-4446-b713-008867b12705n%40googlegroups.com.

Jamie Collinson

unread,
Feb 15, 2021, 4:14:16 AM2/15/21
to Flutter Development (flutter-dev)
Thanks Brett - that's a really useful background doc in an area with which I'm not very familiar, much appreciated.

Apologies if this is a dumb question, but when you say "the usual way around this is to expose a C API from the C++ code", is that something more than what I've done by following the Flutter FFI Tutorial to add `extern "C"`? In this case I'm using a proprietary library (`libsmile.a`) and associated headers (`smile.h`) so can't add anything to the library code itself, but could write a more extensive wrapper if that would solve the issue. At the moment I'm just exporting a (test) high level function which uses the library:

#include <cstdio>
#include "smile.h"
#include "smile_license.h"

extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t test_network_load() {
  DSL_network net;
  int res = net.ReadFile("../Assets/VentureBN.xdsl");
  if (DSL_OKAY != res)
  {
    return res;
  }

  int handle = net.FindNode("Forecast");
  if (handle < 0)
  {
    return handle;
  }

  DSL_node *f = net.GetNode(handle);
  int idx = f->Definition()->GetOutcomesNames()->FindPosition("Moderate");
  if (idx < 0)
  {
    return idx;
  }
  f->Value()->SetEvidence(idx);

  net.UpdateBeliefs();

  handle = net.FindNode("Success");
  if (handle < 0)
  {
    return handle;
  }
  DSL_node *s = net.GetNode(handle);
  const DSL_Dmatrix &beliefs = *s->Value()->GetMatrix();
  const DSL_idArray &outcomes = *s->Definition()->GetOutcomesNames();
  for (int i = 0; i < outcomes.NumItems(); i++)
  {
    printf("%s=%g\n", outcomes[i], beliefs[i]);
  }

  return DSL_OKAY;
}

Reply all
Reply to author
Forward
0 new messages