使用SWIG将二进制数据移入/移出Perl

时间:2011-03-09 20:00:14

标签: perl binary char swig unsigned

我试图让我更容易在Perl和我的C ++库之间移动二进制数据。

我创建了一个c ++结构来处理binary_data:

struct binary_data {
    unsigned long length;
    unsigned char *data;
};

在我的SWIG界面文件中,我有以下内容:

%typemap(in) binary_data * (binary_data temp) {
    STRLEN len;
    unsigned char *outPtr;
    if(!SvPOK($input))
        croak("argument must be a scalar string");
    outPtr = (unsigned char*) SvPV($input, len);
        printf("set binary_data '%s' [%d] (0x%X)\n", outPtr, len, $input);
    temp.data = outPtr;
    temp.length = len;
    $1 = &temp;
}
%typemap(out) binary_data * {
  SV *obj = sv_newmortal();
  if ($1 != 0 && $1->data != 0 && $1->length > 0) {     
    sv_setpvn(obj, (const char*) $1->data, $1->length);
    printf("get binary_data '%s' [%d] (0x%X)\n", $1->data, $1->length, obj);
  } else {
    sv_setsv(obj, &PL_sv_undef);
    printf("get binary_data [set to undef]\n");
  }
  if( !SvPOK(obj) )
    croak("The result is not a scalar string"); 
  $result = obj;
}

我通过“ExtUtils :: MakeMaker”构建我的Perl模块,这一切都很好。

然后我运行以下perl测试脚本以确保二进制数据正在运行 正确设置/获取perl字符串。

my $fr = ObjectThatContainsBinaryData->new();
my $data = "1234567890"; 
print ">>>PERL:swig_data_set\n"; 
$fr->swig_data_set($data);
print "<<<PERL:swig_data_set\n";
print ">>>PERL:swig_data_get\n"; 
my $rdata = $fr->swig_data_get();
print "<<<PERL: swig_data_get\n";
print "sent    :" . \$data . " len=" . length($data). " '$data'\n"
     ."recieved:". \$rdata.  " len=" . length($rdata). " '$rdata'\n";

现在合并的C ++和Perl printf标准输出是:

>>>PERL:swig_data_set
set binary_data '1234567890' [10] (0x12B204D0)
<<<PERL:swig_data_set
>>>PERL:swig_data_get
get binary_data '1234567890' [10] (0x1298E4E0)
<<<PERL: swig_data_get
sent    :SCALAR(0x12b204d0) len=10 '1234567890'
recieved:SCALAR(0x12bc71c0) len=0 ''

那为什么看起来像 sv_setpvn 的perl调用失败或无效? 我不知道为什么当我在perl中打印返回的二进制数据时,它显示为空标量,但在SWIG C ++嵌入式类型映射中看起来很好。

我正在使用:

为x86_64-linux-thread-multi

构建的Perl v5.8.8

SWIG 2.0.1

gcc版本4.1.1 20070105(Red Hat 4.1.1-52)

4 个答案:

答案 0 :(得分:2)

如果替换%typemap(out)中的以下行:

$result = obj;

$result = obj; argvi++;  //This is a hack to get the hidden stack pointer to increment before the return

SWIG生成的代码现在看起来像:

...
  ST(argvi) = obj; argvi++;
}
XSRETURN(argvi);
}

您的测试脚本将按预期返回Perl字符串。

SV = PV(0x1eae7d40) at 0x1eac64d0
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,POK,pPOK)
  PV = 0x1eb25870 "1234567890"\0
  CUR = 10
  LEN = 16
<<<PERL: swig_data_get
sent    :SCALAR(0x1ea64530) len=10 '1234567890'
recieved:SCALAR(0x1eac64d0) len=10 '1234567890'

您应该更仔细地阅读有关Perl中的文字图的SWIG 2.0文档:

” 30.8.2返回值

返回值放在每个包装函数的参数堆栈上。参数堆栈指针的当前值包含在变量argvi中。每当添加新的输出值时,增加该值是至关重要的。对于多个输出值,argvi的最终值应为输出值的总数。 “

答案 1 :(得分:1)

如果你没有使它成为凡人怎么办?我正在使用Inline :: C进行测试(因为我从未使用过SWIG),并且因为Inline :: C正在为我做这件事,因此将SV设置为凡人造成了问题。也许SWIG使用类似的设计?

两个

SV* obj = newSV(0);
sv_setpvn(obj, "abc", 3);

SV* obj = newSVpvn("abc", 3);

使用Inline :: C。

答案 2 :(得分:1)

swig提供了一个名为cdata.i的模块。 您应该在接口定义文件中包含它。

包含此内容后,它会提供两个函数cdata()memmove()。给定void *和二进制数据的长度,cdata()将其转换为目标语言的字符串类型。

memmove()正好相反。给定一个字符串类型,它会将字符串的内容(包括嵌入的空字节)复制到C void *类型中。

使用此模块处理二进制数据变得非常简单。

我希望这是你需要的。

答案 3 :(得分:0)

在Perl方面,你可以添加

use Devel::Peek;
Dump($fr->swig_data_get());

并提供输出?感谢。