\begingroup

我有一个需要调用的外部 c 函数,并且我希望使用 ForeignFunctionLoad。

c 函数 foo 将结构的引用作为参数,例如:

foo(int v, struct * myStruct);

myStruct 本身包含一个数组,例如

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

我怎样才能将这个函数称为 foo?

也许可以用这种方式正确加载它:

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

但是当我调用它时,我需要提供一个指向通过 RawMemoryAllocate 分配的内存的指针。因此我可以通过创建

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

但是,此调用不知道双精度数组的大小,因此会分配一个双精度和一个指向双精度的指针 – 而函数需要该数组,并将写入该数组的 4 个元素。当然,我可以通过以下方式分配一个包含 4 个双精度的数组

arrayInMathematica =  RawMemoryAllocate["CDouble",4];

但我需要以某种方式“嵌套”这两者,即数组位于结构内部。

任何想法?

谢谢你!

斯蒂芬

\endgroup

4

  • \begingroup
    ForeignFunctionLoad看起来很有趣,但每个 API 都必须有一些限制。我认为你最好用 编写一个包装函数LibraryLink。例如,这个包装器将一些数据从 mathematica 复制到结构,然后调用函数foo
    \endgroup


    – 

  • \begingroup
    如果五个双精度数连续位于指针指向的某个已分配数组中p,那么您可以尝试以foo这种方式调用:foo( v, reinterpret_cast< myStruct *>(p) )。这是否可行可能取决于生成库的编译器。如果我没记错的话,C标准不要求structs打包,因此允许编译器放入一些填充。(它可能会尝试将数组放到b一个特殊对齐的地址上以进一步矢量化。)不同的编译器可能对此有不同的策略。
    \endgroup


    – 


  • \begingroup
    谢谢!您是否打算放置一个示例包装器的链接?(“例如,这个包装器…”)?
    \endgroup


    – 

  • \begingroup
    不,我没打算这么做。但既然你这么好心地询问… 😉
    \endgroup


    – 


最佳答案
1

\begingroup

下面是我提到的包装器的示例。我用 Apple Clang 编译了它,它似乎可以工作。但我不能保证它能在所有编译器上正常工作。

顺便说一句,reinterpret_cast需要C++。但它也适用于普通C类型转换。

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"]
   ];

使用示例:

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

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

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

\endgroup