12
$\begingroup$

I have an external c-function I need to call, and I wish to use ForeignFunctionLoad.

The c-function, foo, takes as an argument a reference to a struct, for example:

foo(int v, struct * myStruct);

myStruct itself contains an array, i.e. like

typedef struct {
  double a;
  double b[2][2];
} myStruct;

How can I call this function foo?

One can probably load it correctly in this way:

fooInMathematica = ForeignFunctionLoad["mylibfile","foo",{"CInt",RawPointer::[{"CDouble",RawPointer::["CDouble"]}]}];

But when I call it, I need to provide a pointer to some memory allocated via RawMemoryAllocate. So I could do so by creating

structInMathematica = RawMemoryAllocate[{"CDouble", "RawPointer"::["CDouble"]}];

However, this call does not know the size of the array of doubles, and would allocate a double and a pointer to a double - while the function needs the array, and would write into the 4 elements of the array. I can, of course allocate an array of 4 doubles via

arrayInMathematica =  RawMemoryAllocate["CDouble",4];

But I would need to somehow "nest" the two, i.e. the array lives inside the struct.

Any idea?

Thank you!

Stefan

$\endgroup$
6
  • 1
    $\begingroup$ ForeignFunctionLoad looks interesting, but every API has to have some limits. I think you would be better off by writing a wrapper functions with LibraryLink. For example, this wrapper copies some data from mathematica to the struct and then calls the function foo. $\endgroup$ Commented Jul 5 at 8:54
  • $\begingroup$ If the five doubles lie consecutively in some allocated array to which the pointer p points, then you might try to call foo this way: foo( v, reinterpret_cast< myStruct *>(p) ). Whether that works or not might depend on the compiler that produced the library. IIRC, the C standard does not mandate structs to be packed, so the compiler is allowed to put in some padding. (It might try to put the array b onto a specially aligned address in order to further vectorization.) And different compiler might have different strategies to this. $\endgroup$ Commented Jul 5 at 8:56
  • $\begingroup$ Thank you! Did you intend to put a link to an example wrapper? ("For example, this wrapper ...") ? $\endgroup$ Commented Jul 5 at 9:03
  • 1
    $\begingroup$ No, I did not intend to. But since you asked so nicely... ;) $\endgroup$ Commented Jul 5 at 9:21
  • $\begingroup$ Thanks! In the end the answer from Ben Izd made it work for me! $\endgroup$ Commented Jul 8 at 12:16

3 Answers 3

9
$\begingroup$

Assume we have the following function (just add 1 to each element):

struct sample
{
    double a;
    double b[2][2];
};


extern "C" __declspec(dllexport) void test(sample *s) {
    s->a = s->a + 1;
    s->b[0][0] = s->b[0][0] + 1;
    s->b[0][1] = s->b[0][1] + 1;
    s->b[1][0] = s->b[1][0] + 1;
    s->b[1][1] = s->b[1][1] + 1;
    
}

We can specify structs in ForeignFunctionLoad like below:

fn = ForeignFunctionLoad[
  "C:\\sample.dll", 
  "test", {"RawPointer"::[{"CDouble", {{"CDouble", 
        "CDouble"}, {"CDouble", "CDouble"}}}]} -> "Void"]

which uses nested types of TypeSpecifier["ListTuple"].

For providing input we can use the following:

input = RawMemoryExport[{{0., {{1., 2.}, {3., 4.}}}},
                        {"CDouble", {{"CDouble", "CDouble"}, {"CDouble", "CDouble"}}}];

We can read the struct or pass it to our function like below:

RawMemoryRead[input]
(* Out: {0., {{1., 2.}, {3., 4.}}} *)

fn[input]

RawMemoryRead[input]
(* Out: {1., {{2., 3.}, {4., 5.}}} *)

Manipulation

The only thing I couldn't manage to do easily is to manipulate a specific part of the struct.

  1. (Easiest) You can have to specify a whole new struct:
RawMemoryWrite[input, {10., {{11., 12.}, {13., 14.}}}]

(* Out: RawPointer[...] *)

RawMemoryRead[input]
(* Out: {10., {{11., 12.}, {13., 14.}}} *)
  1. We can declare a similar structure using TypeDeclaration like below:
t = TypeDeclaration["Product", 
   "sample", <|"a" -> "CDouble", "e1" -> "CDouble", "e2" -> "CDouble",
     "e3" -> "CDouble", "e4" -> "CDouble"|>];

Then write a wrapper to receive the data, cast it to struct we defined and manipulate it:

fn2 = FunctionCompile[{t}, 
  Function[{Typed[a, 
     "RawPointer"::[
      "ListTuple"::["CDouble", 
       "ListTuple"::["ListTuple"::["CDouble", "CDouble"], 
        "ListTuple"::["CDouble", "CDouble"]]]]]},
   With[{aCasted = Cast[a, "sample", "BitCast"]},
    aCasted["e4"] = 123.;
    ]
   ]]
RawMemoryRead[input]
(* Out: {0., {{1., 2.}, {3., 4.}}} *)

fn2[input["Value"]]

RawMemoryRead[input]
(* Out: {0., {{1., 2.}, {3., 123.}}} *)

Keep in mind that most of the functions used in this post are experimental and with my limited knowledge, I'm not sure this approach would be robust enough for different situations.

$\endgroup$
1
  • $\begingroup$ Thank you very much! With this I was able to get my code to run as it should! $\endgroup$ Commented Jul 8 at 12:15
11
$\begingroup$

Here is an example of the wrapper that I mentioned. I compiled it with Apple Clang, and it seems to work. But I don't give any guarantees that it will work properly with all compilers.

Btw. reinterpret_cast requires C++. But it also works with plain C type casts.

Needs["CCompilerDriver`"];
Quiet[LibraryFunctionUnload[cf]];
ClearAll[cf];
cf = Module[{lib, code, filename, name, time}, 
   name = "cf";
   filename = name;
   code = StringJoin["
#include \"WolframLibrary.h\"

typedef struct {
  double a;
  double b[2][2];
} myStruct;

void foo( int v, myStruct *  s )
{
    s->a = v;
    s->b[0][0] = v;
    s->b[0][1] = v;
    s->b[1][0] = v;
    s->b[1][1] = v;
}

EXTERN_C DLLEXPORT int " <> name <> "(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res)
{
    int      v     = (int)(MArgument_getInteger(Args[0]));
    MTensor  input = MArgument_getMTensor(Args[1]);
    double * p     = libData->MTensor_getRealData(input);

    foo( v, (myStruct*)(p) );

    libData->MTensor_disown(input);
    
    return LIBRARY_NO_ERROR;
}"];
   Print["Compiling " <> name <> "..."];
   time = 
    AbsoluteTiming[
      lib = CreateLibrary[code, filename, "Language" -> "C", 
         "TargetDirectory" -> $TemporaryDirectory, 
         "ShellOutputFunction" -> Print
         ];
      ][[1]];
   Print["Compiling " <> name <> " done. Time elapsed = " <> ToString[time] <> "s."];
   LibraryFunctionLoad[lib, name, {Integer, {Real, 1, "Shared"}}, "Void"]
   ];

Usage example:

p = ConstantArray[0., 5];
cf[1, p]
p
cf[2, p]
p

{1., 1., 1., 1., 1.}

{2., 2., 2., 2., 2.}

$\endgroup$
1
$\begingroup$
foo(int v, struct * myStruct);

typedef struct {
  double a;
  double b[2][2];
} myStruct;

You mixing two things here. one is a typedeffed struct and one is a function call with a struct.

Your function call should be:

foo(int v, myStruct *structVar);

if you want a pointer to the struct or omitting the reference * if you want to send the the struct on the stack.

alternatively changing the struct definition to:

struct myStruct {
   ...
};

and the function definition to:

foo(int v, struct myStruct *structVar);
$\endgroup$
1
  • $\begingroup$ Ah, yes, of course. Thanks! (This was never meant to be a working example, but to explain my question about allocation the memory.) $\endgroup$ Commented Jul 8 at 6:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.