2011-03-09 28 views
4

我想讓我在Perl和C++庫之間移動二進制數據變得簡單。使用SWIG將二進制數據移入/移出Perl

我創建了一個C++結構,以一手binary_data:

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

在我痛飲接口文件,因爲我有以下幾點:

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

我建我的Perl模塊通過「的ExtUtils :: MakeMaker的「這一切都很好。

然後,我運行下面的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 stdout是:

>>>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 '' 

那麼,爲什麼它看起來像Perl的調用sv_setpvn失敗或不工作? 我不知道爲什麼當我在perl中打印返回的二進制數據時,它顯示爲空標量,但在SWIG C++嵌入式類型圖中看起來很好。

我使用:

的Perl v5.8.8爲x86_64的Linux的線程多

痛飲內置2.0.1

gcc版本4.1.1 20070105(紅帽4.1.1 -52)

回答

2

如果更換的下面一行在%類型表(出):

$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更加仔細地閱讀typemaps的夜風2.0文檔:

「 30.8.2返回值

返回值被放置在每個包裝函數的參數堆棧上。參數堆棧指針的當前值包含在一個變量argvi中,每當添加一個新的輸出值時,增加該值至關重要,對於多個輸出值,argvi的最終值應該是輸出值的總數。 「

0

在Perl的一面,你可以添加

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

,並提供輸出?謝謝。

+0

得到binary_data '1234567890'[10](0x186BC4E0) 用途:傑韋利::皮克::轉儲(SV,LIM = 4)在./Test_swig_binary.t管線20 – user297500

+0

@ user297500,這是因爲如果沒有在全部退還。這怎麼可能?!如果你上傳了一個tarball,我會試試看。 – ikegami

+1

'$ result = obj; // HACK:SWIG您支持這麼做,或者至少是文檔IT //這是swig生成的perl正常工作所必需的。 argvi ++;'//現在有效,謝謝你的幫助。添加「argvi ++;」 「$ result = obj」之後的語句糾正了問題。我注意到用SWIG創建的所有其他getter方法都將此語句放在包裝cxx代碼中。 – user297500

1

如果你沒有成爲凡人,該怎麼辦?我正在使用Inline :: C進行測試(因爲我從未使用過SWIG),並且將SV設置爲凡人導致了問題,因爲Inline :: C正在爲我做這件事。也許SWIG使用類似的設計?

兩個

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

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

曾與內聯中:C。

+0

謝謝。我嘗試用這兩個不同的構造函數方法替換new_mortal調用,並且仍然得到相同的結果。 – user297500

1

swig提供了一個名爲cdata.i的模塊。 您應該將其包含在接口定義文件中。

一旦你包括這個,它提供了兩個功能cdata()memmove()。給定void *和二進制數據的長度,cdata()將其轉換爲目標語言的字符串類型。

memmove()是相反的。給定一個字符串類型,它會將字符串的內容(包括嵌入的空字節)複製到C void *類型中。

使用此模塊處理二進制數據變得非常簡單。

我希望這是你所需要的。