\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
最佳答案
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
|
ForeignFunctionLoad
看起来很有趣,但每个 API 都必须有一些限制。我认为你最好用 编写一个包装函数LibraryLink
。例如,这个包装器将一些数据从 mathematica 复制到结构体,然后调用函数foo
。\endgroup
–
如果五个双精度数连续位于指针指向的某个已分配数组中
p
,那么您可以尝试以foo
这种方式调用:foo( v, reinterpret_cast< myStruct *>(p) )
。这是否可行可能取决于生成库的编译器。如果我没记错的话,C标准不要求structs
打包,因此允许编译器放入一些填充。(它可能会尝试将数组放到b
一个特殊对齐的地址上以进一步矢量化。)不同的编译器可能对此有不同的策略。\endgroup
–
谢谢!您是否打算放置一个示例包装器的链接?(“例如,这个包装器…”)?
\endgroup
–
不,我没打算这么做。但既然你这么好心地询问… 😉
\endgroup
–
|