Interfacing with a C++ library: strange behaviour

,

This is a long shot, but I have absolutely no idea what is happening here…

So I try to write a wrapper for a few functions in the (C++, header-only) libigl geometry library. For this I write an intermediary C library which instantiates the templates and presents an API useable by Julia. While writing this I encounter some strange behaviour: the following test() function works as expected when called from a stand-alone C program, but when called from Julia (as ccall((:test, "iglwrap/libiglwrap.so"), Cvoid, (), )), it throws an exception (signal(6): Aborted). The function has no parameters, no memory is passed between the C/C++ and Julia sides, so I have no idea why the fact that it is called from within Julia causes it to crash.

void test(void) {
using namespace Eigen;

Matrix<double, Dynamic, 3> tv1(26, 3), tv2(8,3);
Matrix<int, Dynamic, 3> tf1(48, 3), tf2(12,3);
// vertices of a cylinder, with extra precision to be *certain* this is identical to the values computed from the Julia program:
tv1 << 3, 0, 0, 2.656368076959629842548338274355, 1.3941695161313054640572772768792, 0, 1.7041942401934677686625718706637, 2.4689515976809692787696803861763, 0, 0.3616100407659696425177742185042, 2.9781266222941620291919662122382, 0, -1.0638146611276062536433073546505, 2.8050487280562448333398606337141, 0, -2.2455322445133027997599128866568, 1.9893679747223862452898401897983, 0, -2.9128254522781560353905661031604, 0.71794699286267404936268121673493, 0, -2.9128254522781560353905661031604, -0.71794699286267404936268121673493, 0, -2.2455322445133027997599128866568, -1.9893679747223862452898401897983, 0, -1.0638146611276062536433073546505, -2.8050487280562448333398606337141, 0, 0.3616100407659696425177742185042, -2.9781266222941620291919662122382, 0, 1.7041942401934677686625718706637, -2.4689515976809692787696803861763, 0, 2.656368076959629842548338274355, -1.3941695161313054640572772768792, 0, 3, 0, 10, 2.656368076959629842548338274355, 1.3941695161313054640572772768792, 10, 1.7041942401934677686625718706637, 2.4689515976809692787696803861763, 10, 0.3616100407659696425177742185042, 2.9781266222941620291919662122382, 10, -1.0638146611276062536433073546505, 2.8050487280562448333398606337141, 10, -2.2455322445133027997599128866568, 1.9893679747223862452898401897983, 10, -2.9128254522781560353905661031604, 0.71794699286267404936268121673493, 10, -2.9128254522781560353905661031604, -0.71794699286267404936268121673493, 10, -2.2455322445133027997599128866568, -1.9893679747223862452898401897983, 10, -1.0638146611276062536433073546505, -2.8050487280562448333398606337141, 10, 0.3616100407659696425177742185042, -2.9781266222941620291919662122382, 10, 1.7041942401934677686625718706637, -2.4689515976809692787696803861763, 10, 2.656368076959629842548338274355, -1.3941695161313054640572772768792, 10;
tv2 << 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 3, 3, 3, 0, 0, 3, 0, 3, 3, 3, 0, 3, 3, 3;
tf1 << 8, 7, 5, 5, 0, 8, 6, 5, 7, 3, 5, 4, 10, 9, 8, 12, 11, 10, 0, 12, 10, 1, 3, 2, 3, 1, 0, 3, 0, 5, 10, 8, 0, 18, 20, 21, 21, 13, 18, 20, 18, 19, 17, 18, 16, 21, 22, 23, 23, 24, 25, 23, 25, 13, 15, 16, 14, 13, 14, 16, 18, 13, 16, 13, 21, 23, 0, 1, 13, 1, 2, 14, 2, 3, 15, 3, 4, 16, 4, 5, 17, 5, 6, 18, 6, 7, 19, 7, 8, 20, 8, 9, 21, 9, 10, 22, 10, 11, 23, 11, 12, 24, 12, 0, 25, 1, 14, 13, 2, 15, 14, 3, 16, 15, 4, 17, 16, 5, 18, 17, 6, 19, 18, 7, 20, 19, 8, 21, 20, 9, 22, 21, 10, 23, 22, 11, 24, 23, 12, 25, 24, 0, 13, 25;
tf2 << 5, 4, 6, 6, 7, 5, 6, 2, 3, 3, 7, 6, 3, 1, 5, 5, 7, 3, 4, 0, 2, 2, 6, 4, 1, 0, 4, 4, 5, 1, 2, 0, 1, 1, 3, 2;
  Matrix<double,Dynamic,3> v3;
  Matrix<int,Dynamic,3> f3;
  VectorXi j;
  igl::copyleft::cgal::mesh_boolean(tv1,tf1,tv2,tf2,igl::MeshBooleanType(0),
      v3, f3, j);
  std::cout << "ok, computed " << v3.rows() << " vertices and " << f3.rows() << " faces\n";
}

Any idea what is happening here? gdb was not too helpful (it points me to a nondescript position in libgcc_s.so.1).

ccall((:test, “iglwrap/libiglwrap.so”), Cvoid, (), )

Should this be ccall((:test, "iglwrap/libiglwrap.so"), Cvoid, ())? (note the comma)

That comma is an end-of-tuple comma; its presence (or absence) does not change anything (it is there because I removed some arguments from the call and left the comma).

it throws an exception ( signal(6): Aborted ).

If the C++ code throws an exception and your C interfacing code does not catch/handle that exception, then abort() is called by the default termination handler as a result of the unhandled exception.

See std::terminate - cppreference.com

I think I got it working: instead of ccall((:function, "library.so")) I did an explicit dlopen("library.so", RTLD_NOW|RTLD_LOCAL) followed by dlsym(library_handle, :function).

(Not being very knowledgeable about dynamic libraries, I chose the flags more or less randomly, as long as (1) they were different from the default values that caused the crash, and (2) they were probably close to what happens when directly linking an object file with library.so).