1

I am trying to call a C++ wrapper function from dotnet service on Linux the first time.

C++ code:

extern "C" std::string myfunc(int a, int b){

          std::string mystring = funcB(a, b);
          return mystring;

}

c# code:

public string myCsharpFunc getA(int a, int b){
     return callMyFunc(a, b);
}
[DllImport("xxxx.so", EntryPoint ="myfunc", CallingConvention= CallingConvertion.Cdecl)]
private static extern string callMyfunc(int a, int b);

The dotnet service is running fine and I am able to do a test GET. I could also run into myFunc and the return from funcB looked correct. But everything crashed when mystring is returned with a segmentation fault (core dump). It seems like even if I made mystring returned as "test", the program crashed the same way. What did I miss?

  • 1
    A C# string ≠ a C++ std::string – Eljay Jan 18 '22 at 23:07
  • I tried to cast it (string) callMyFunc(a,b) to a different string. did not change anything. – NotArobot20220118 Jan 18 '22 at 23:17
  • Have you tried using a `char*` instead of `std::string` as the exposed API/ABI? I am not even sure how you can expose `std::string` as `extern C`; it's not callable from `C` even though `extern C` would make you think it is. See https://gcc-help.gcc.gnu.narkive.com/DquFh2uv/warning-for-incompatible-functions-declared-extern-c for details. A `char*` is also automatically converted to a C# `string`: https://learn.microsoft.com/en-us/dotnet/standard/native-interop/type-marshaling – omajid Jan 18 '22 at 23:27
  • Thanks, omajid! Yes, converting returning type to char* does the trick! I just did a strcpy of the string returning from FuncB to a char* and return it. No more segmentation fault errors and the right string was returned. – NotArobot20220118 Jan 18 '22 at 23:54

1 Answers1

0

If you compile this code using clang 13, you get a very interesting warning:

warning: 'myfunc' has C-linkage specified, but returns user-defined type 'std::string' (aka 'basic_string<char>') which is incompatible with C [-Wreturn-type-c-linkage]
extern "C" std::string myfunc(int a, int b){
                       ^

(An easy way to compile your code against a ton of compilers and other tools to see what various compilers and tools think about your code is https://godbolt.org/).

You are using extern "C" to say "this function should be linked and made available in a way that it can be called from any C-like API". But since std::string is a C++ class, it isn't something that can be used from C. extern "C", despite what it suggests, doesn't really work.

And when you try and invoke this function through .NET it doesn't work and fails in all sorts of interesting way.

You should use a data type that has a valid C-linkage. Probably a char* here. The type marshalling in .NET will automatically convert a C char* to a C# string and the other way around.

There's some subtlety to memory allocation too. If you return a char * that was allocated via malloc, you will have to take special care to free it correctly. See pinvoke: How to free a malloc'd string? for more details.

omajid
  • 14,165
  • 4
  • 47
  • 64