我正在使用 XS 在 Windows 上的 Cygwin 下使用 g++ 从我的 Perl 项目调用第三方 DLL。 其中一个 DLL 函数采用结构体作为参数,并以指向结构体的指针形式返回其主要结果。 现在,我传入一个包含 28 个整数的平面列表并填充第一个结构体。 然后我调用该函数。 然后我想将生成的结构展平为最多 54 个整数的列表。
(这看起来像是很多整数,但是DLL函数相当复杂并且需要很长时间才能运行,所以我认为这是值得的。除非有人有更好的主意?)
这已经接近工作了。 我可以说结果大多是合理的。 但有两个奇怪的问题。
当我打印出相同的变量时,根据它是否在“for”循环中,我会得到不同的结果! 我在下面展示了这一点。 我已经盯着这个看了很久了。
当我到达第一个 XPUSH 时,我就得到“内存不足”。
这是XS代码。
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "dll.h"
MODULE = Bridge::Solver::DDS_IF PACKAGE = Bridge::Solver::DDS_IF
PROTOTYPES: ENABLE
void
SolveBoard(inlist)
SV * inlist
INIT:
struct deal dl;
struct futureTricks fut;
int target, solutions, mode, thrId;
int i, j, ret;
if ((! SvROK(inlist)) ||
(SvTYPE(SvRV(inlist)) != SVt_PVAV) ||
av_len((AV *) SvRV(inlist)) != 27)
{
XSRETURN_UNDEF;
}
printf("New INIT OK\n");
PPCODE:
dl.trump = SvIV(*av_fetch((AV *)SvRV(inlist), 0, 0));
dl.first = SvIV(*av_fetch((AV *)SvRV(inlist), 1, 0));
dl.currentTrickSuit[0] = SvIV(*av_fetch((AV *)SvRV(inlist), 2, 0));
dl.currentTrickSuit[1] = SvIV(*av_fetch((AV *)SvRV(inlist), 3, 0));
dl.currentTrickSuit[2] = SvIV(*av_fetch((AV *)SvRV(inlist), 4, 0));
dl.currentTrickRank[0] = SvIV(*av_fetch((AV *)SvRV(inlist), 5, 0));
dl.currentTrickRank[1] = SvIV(*av_fetch((AV *)SvRV(inlist), 6, 0));
dl.currentTrickRank[2] = SvIV(*av_fetch((AV *)SvRV(inlist), 7, 0));
dl.remainCards[0][0] = SvUV(*av_fetch((AV *)SvRV(inlist), 8, 0));
dl.remainCards[0][1] = SvUV(*av_fetch((AV *)SvRV(inlist), 9, 0));
dl.remainCards[0][2] = SvUV(*av_fetch((AV *)SvRV(inlist), 10, 0));
dl.remainCards[0][3] = SvUV(*av_fetch((AV *)SvRV(inlist), 11, 0));
dl.remainCards[1][0] = SvUV(*av_fetch((AV *)SvRV(inlist), 12, 0));
dl.remainCards[1][1] = SvUV(*av_fetch((AV *)SvRV(inlist), 13, 0));
dl.remainCards[1][2] = SvUV(*av_fetch((AV *)SvRV(inlist), 14, 0));
dl.remainCards[1][3] = SvUV(*av_fetch((AV *)SvRV(inlist), 15, 0));
dl.remainCards[2][0] = SvUV(*av_fetch((AV *)SvRV(inlist), 16, 0));
dl.remainCards[2][1] = SvUV(*av_fetch((AV *)SvRV(inlist), 17, 0));
dl.remainCards[2][2] = SvUV(*av_fetch((AV *)SvRV(inlist), 18, 0));
dl.remainCards[2][3] = SvUV(*av_fetch((AV *)SvRV(inlist), 19, 0));
dl.remainCards[3][0] = SvUV(*av_fetch((AV *)SvRV(inlist), 20, 0));
dl.remainCards[3][1] = SvUV(*av_fetch((AV *)SvRV(inlist), 21, 0));
dl.remainCards[3][2] = SvUV(*av_fetch((AV *)SvRV(inlist), 22, 0));
dl.remainCards[3][3] = SvUV(*av_fetch((AV *)SvRV(inlist), 23, 0));
target = SvIV(*av_fetch((AV *)SvRV(inlist), 24, 0));
solutions = SvIV(*av_fetch((AV *)SvRV(inlist), 25, 0));
mode = SvIV(*av_fetch((AV *)SvRV(inlist), 26, 0));
thrId = SvIV(*av_fetch((AV *)SvRV(inlist), 27, 0));
ret = SolveBoard(dl, target, solutions, mode, &fut, thrId);
printf("Return code %d\n", ret);
printf("Nodes %d\n", fut.nodes);
printf("Cards %d\n", fut.cards);
printf("%6s %12s %12s %12s %12s\n",
"", "suit", "rank", "equals", "score");
printf("%6d %12d %12d %12d %12d\n\n",
0, fut.suit[0], fut.rank[0], fut.equals[0], fut.score[0]);
for (i = 0; i < 13; i++)
{
printf("%6d %12d %12d %12d %12d\n",
i, fut.suit[i], fut.rank[i], fut.equals[i], fut.score[i]);
}
printf("\n%6d %12d %12d %12d %12d\n\n",
0, fut.suit[0], fut.rank[0], fut.equals[0], fut.score[0]);
printf("Trying to push nodes\n");
XPUSHs(sv_2mortal(newSViv(fut.nodes)));
printf("Trying to push cards\n");
XPUSHs(sv_2mortal(newSViv(fut.cards)));
printf("Trying to loop\n");
for (i = 0; i <= 12; i++)
{
XPUSHs(sv_2mortal(newSViv(fut.suit [i])));
XPUSHs(sv_2mortal(newSViv(fut.rank [i])));
XPUSHs(sv_2mortal(newSViv(fut.equals[i])));
XPUSHs(sv_2mortal(newSViv(fut.score [i])));
}
printf("Done looping\n");
这是DLL头文件的相关部分。
struct futureTricks
{
int nodes;
int cards;
int suit[13];
int rank[13];
int equals[13];
int score[13];
};
struct deal
{
int trump;
int first;
int currentTrickSuit[3];
int currentTrickRank[3];
unsigned int remainCards[4][4];
};
extern "C" int SolveBoard(
struct deal dl,
int target,
int solutions,
int mode,
struct futureTricks *futp,
int threadIndex);
这是输出。 返回码就OK了。 节点和卡则不然。 如果你眯着眼睛,你可能会注意到输出表中也出现了 0 和 768,所以可能存在某种偏移。
第一个奇怪的事情是主表前后的两行“0”与主表中的“0”行不同。 不过,主表中的数据符合预期,包括第 10-12 行中的垃圾。
第二个问题是 XPUSH 没有达到预期效果。
New INIT OK
Return code 1
Nodes 0
Cards 768
suit rank equals score
0 0 2 -2147319000 -2147296756
0 2 2 0 2
1 2 6 0 2
2 2 10 768 2
3 2 13 0 2
4 3 14 0 2
5 0 6 0 1
6 0 10 512 1
7 0 13 0 1
8 3 4 0 0
9 3 11 0 0
10 1773292640 -2147056120 4 -2147319000
11 1772354411 0 -2146989552 -2146837752
12 8192 35 2665016 -2147319000
0 0 2 -2147319000 -2147296756
Trying to push nodes
Out of memory!
确实是堆栈的问题。
提供的dll.h测试了
_WIN32
和#define
'd STDCALL
到__stdcall
下的_WIN32
,否则为空。
Cygwin 下的 g++ 不会发出
_WIN32
,所以我猜调用约定默认为 __cdecl
。
手动定义
_WIN32
会产生很多其他错误,但我在 dll.h 中添加了对 \__CYGWIN__
的测试,编译器确实会发出该测试,并将其提供给作者以用于他的下一个版本。
这是一个非常令人沮丧的错误,所以我希望这可以帮助将来的其他人。 你永远不知道...
对于偏移量问题,可能是因为 Perl 与 C 变量定义的关系非常糟糕。
在所有其他内容之前包含 dll.h 可能会解决这个问题。