开发者

Moving binary data to/from Perl using SWIG

开发者 https://www.devze.com 2023-02-16 08:04 出处:网络
I\'m trying to make it easy for me to move binary data between Perl and my C++ library. I created a c++ struct to hand the binary_data:

I'm trying to make it easy for me to move binary data between Perl and my C++ library.

I created a c++ struct to hand the binary_data:

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

In my SWIG interface file for I have the following:

%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;
}

I build my Perl module via "ExtUtils::MakeMaker" and it's all good.

I then run the following perl test script to ensure the binary data is being set/get from a perl string correctly.

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

Now the combined C++ and Perl printf stdout is:

>>>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开发者_Go百科) len=10 '1234567890'
recieved:SCALAR(0x12bc71c0) len=0 ''

So why does it look like the perl call to sv_setpvn is failing or not working? I don't know why when I print the returned binary data in perl, it shows as an empty scalar, but it looks fine within the SWIG C++ embedded typemap.

I'm using:

Perl v5.8.8 built for x86_64-linux-thread-multi

SWIG 2.0.1

gcc version 4.1.1 20070105 (Red Hat 4.1.1-52)


If you replace the following line of in your %typemap(out):

$result = obj;

With

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

The SWIG Generated code will now look like:

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

And your test script will return the Perl String as expected.

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'

You should have read the SWIG 2.0 documentation on typemaps in Perl more closely:

" 30.8.2 Return values

Return values are placed on the argument stack of each wrapper function. The current value of the argument stack pointer is contained in a variable argvi. Whenever a new output value is added, it is critical that this value be incremented. For multiple output values, the final value of argvi should be the total number of output values. "


What if you don't make it mortal? I was doing testing with Inline::C (since I've never used SWIG), and setting the SV to mortal caused problems since Inline::C was doing it for me. Perhaps SWIG uses a similar design?

Both

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

and

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

worked with Inline::C.


swig provides a module named cdata.i. You should include this in the interface definition file.

Once you include this, it gives two functions cdata() and memmove(). Given a void * and the length of the binary data, cdata() converts it into a string type of the target language.

memmove() is the reverse. given a string type, it will copy the contents of the string(including embedded null bytes) into the C void* type.

Handling binary data becomes much simple with this module.

I hope this is what you need.


On the Perl side, could you add

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

and provide the output? Thanks.

0

精彩评论

暂无评论...
验证码 换一张
取 消