我把你的C++函數爲頭文件作爲測試一個內聯函數。
我然後能夠構建一個SWIG接口,它可以完成您正在尋找的任務。它有兩個關鍵部分。首先,我寫了一個類型映射,它允許將std::map
,或作爲perl散列作爲輸入給期望std::map
的C++函數。在後者的情況下,它會根據perl散列構建一個臨時映射來用作參數。 (這很方便但可能很慢)。 typemap通過檢查實際傳入的內容來選擇正確的行爲。
該解決方案的第二部分是將一些C++映射的成員函數映射到perl用於在散列上重載操作的特殊函數。其中大多數都是簡單地用%rename
實現的,其中C++函數和perl函數是兼容的,但FIRSTKEY
和NEXTKEY
不能很好地映射到C++的迭代器,因此這些是使用%extend
和(內部)另一個std::map
來實現的,以存儲映射的迭代狀態我們正在包裝。
這裏沒有用於返回地圖的特殊類型映射,但是通過現在實現的特殊操作還有額外的行爲。
的SWIG接口看起來像:
%module stl
%include <std_string.i>
%include <exception.i>
%rename(FETCH) std::map<std::string, std::string>::get;
%rename(STORE) std::map<std::string, std::string>::set;
%rename(EXISTS) std::map<std::string, std::string>::has_key;
%rename(DELETE) std::map<std::string, std::string>::del;
%rename(SCALAR) std::map<std::string, std::string>::size;
%rename(CLEAR) std::map<std::string, std::string>::clear;
%{
#include <map>
#include <string>
// For iteration support, will leak if iteration stops before the end ever.
static std::map<void*, std::map<std::string, std::string>::const_iterator> iterstate;
const char *current(std::map<std::string, std::string>& map) {
std::map<void*, std::map<std::string, std::string>::const_iterator>::iterator it = iterstate.find(&map);
if (it != iterstate.end() && map.end() == it->second) {
// clean up entry in the global map
iterstate.erase(it);
it = iterstate.end();
}
if (it == iterstate.end())
return NULL;
else
return it->second->first.c_str();
}
%}
%extend std::map<std::string, std::string> {
std::map<std::string, std::string> *TIEHASH() {
return $self;
}
const char *FIRSTKEY() {
iterstate[$self] = $self->begin();
return current(*$self);
}
const char *NEXTKEY(const std::string&) {
++iterstate[$self];
return current(*$self);
}
}
%include <std_map.i>
%typemap(in,noblock=1) const std::map<std::string, std::string>& (void *argp=0, int res=0, $1_ltype tempmap=0) {
res = SWIG_ConvertPtr($input, &argp, $descriptor, %convertptr_flags);
if (!SWIG_IsOK(res)) {
if (SvROK($input) && SvTYPE(SvRV($input)) == SVt_PVHV) {
fprintf(stderr, "Convert HV to map\n");
tempmap = new $1_basetype;
HV *hv = (HV*)SvRV($input);
HE *hentry;
hv_iterinit(hv);
while ((hentry = hv_iternext(hv))) {
std::string *val=0;
// TODO: handle errors here
SWIG_AsPtr_std_string SWIG_PERL_CALL_ARGS_2(HeVAL(hentry), &val);
fprintf(stderr, "%s => %s\n", HeKEY(hentry), val->c_str());
(*tempmap)[HeKEY(hentry)] = *val;
delete val;
}
argp = tempmap;
}
else {
%argument_fail(res, "$type", $symname, $argnum);
}
}
if (!argp) { %argument_nullref("$type", $symname, $argnum); }
$1 = %reinterpret_cast(argp, $ltype);
}
%typemap(freearg,noblock=1) const std::map<std::string, std::string>& {
delete tempmap$argnum;
}
%template(StringStringMap) std::map<std::string, std::string>;
%{
#include "stl.h"
%}
%include "stl.h"
我再適應你的樣品Perl來測試:
use Data::Dumper;
use stl;
my $v = stl::TryMap(stl::StringStringMap->new());
$v->{'a'} = '1';
print Dumper $v;
print Dumper stl::TryMap({'a' => '4'});
print Dumper stl::TryMap($v);
foreach my $key (keys %{$v}) {
print "$key => $v->{$key}\n";
}
print $v->{'7'}."\n";
這讓我能夠成功運行:
Got map: 0x22bfb80
$VAR1 = bless({
'7' => '!',
'a' => '1'
}, 'stl::StringStringMap');
Convert HV to map
a => 4
Got map: 0x22af710
In C++ map: a => 4
$VAR1 = bless({
'7' => '!',
'a' => '4'
}, 'stl::StringStringMap');
Got map: 0x22bfb20
In C++ map: 7 => !
In C++ map: a => 1
$VAR1 = bless({
'7' => '!',
'a' => '1'
}, 'stl::StringStringMap');
7 => !
a => 1
!
你可以也將這個對象綁定到散列,例如:
use stl;
my $v = stl::TryMap(stl::StringStringMap->new());
print "$v\n";
tie %foo, "stl::StringStringMap", $v;
print $foo{'a'}."\n";
print tied(%foo)."\n";
從理論上講,你可以編寫一個out typemap來從每個函數調用返回時自動設置這個關係,但到目前爲止,我還沒有成功編寫一個兼容捆綁和SWIG運行時類型系統的類型映射。
應該指出,這不是生產就緒代碼。內部映射有一個線程安全問題,我也知道一些錯誤處理也不存在。我還沒有完全測試所有來自Perl方面的散列操作,超出了上面所看到的。通過與宏進行交互也可以使它更通用。最後,我不是一個perl大師,我也沒有使用過C API,所以在這個領域的一些小心會是有序的。
您的Perl代碼正在將引用傳遞給散列。我根本不瞭解SWIG,但我認爲這很重要。如果您將C++端改爲期望指針會發生什麼? –
我有TryVector是類似於TryMap(期望參考),我使用它像'打印Dumper stl :: TryVector([1,2]);''和一切工作正常 – alexanderkuk
我只注意到一個錯字:**'stl :: TryMap({'a'=>'4});'**你的原始代碼是否有這樣的? –