【问题标题】:STL map in Perl using SWIG使用 SWIG 在 Perl 中映射 STL
【发布时间】:2012-06-24 19:25:09
【问题描述】:

这是我的question on SWIG mailing list 的副本。

我正在尝试在我的 SWIG 绑定中使用 stl 容器。除了 Perl 中的 stl 映射处理之外,一切都运行良好。在 C++ 方面,我有

std::map<std::string, std::string> TryMap(const std::map<std::string, std::string> &map) {
  std::map<std::string, std::string> modified(map);
  modified["7"] = "!";
  return modified;
}

SWIG 配置如下所示

%module stl

%include "std_string.i"

%include "std_map.i"
%template(StringStringMap) std::map<std::string, std::string>;

%{
  #include "stl.h"
%}

%include "stl.h"

在我的 Python 脚本中,我可以这样调用 TryMap

print dict(stl.TryMap({'a': '4'}))

得到漂亮的输出

{'a': '4', '7': '!'}

但我在 Perl 中调用

print Dumper stl::TryMap({'a' => '4'});

得到一个错误

TypeError in method 'TryMap', argument 1 of type 'std::map< std::string,std::string > const &' at perl.pl line 7.

我实际上可以做类似的事情

my $map = stl::TryMap(stl::StringStringMap->new());
print $map->get('7');

然后得到 '!',但这不是一个选项,因为有很多使用“TryMap”的遗留代码期望正常的 Perl 哈希作为其输出。

我相信有办法解决这个问题,因为如果我使用 stl 向量和字符串而不是映射,SWIG 可以在 Python 甚至 Perl 中很好地解决这个特殊问题。

有没有办法在 SWIG 中使用 Perl 处理 stl 映射?我正在使用最新的 SWIG 2.0.7

更新 perl5/std_map.i 可能有问题。太短了=)

$ wc -l perl5/std_map.i python/std_map.i 
   74 perl5/std_map.i
  305 python/std_map.i

【问题讨论】:

  • 您的 Perl 代码正在传递对哈希的引用。我根本不知道 SWIG,但我认为这很重要。如果将 C++ 端更改为期望指针会发生什么?
  • 我有 TryVector,它类似于 TryMap(期望参考),我使用它就像 'print Dumper stl::TryVector([1, 2]);'一切正常
  • 我刚注意到一个错字:stl::TryMap({'a' =&gt; '4}); 你的原始代码有吗?
  • 问:你更喜欢高效还是直观?可以编写一些你想要的东西,但这意味着为每次调用创建、填充和返回一个新的散列,而-&gt;{'key'} 语法可以在没有大量副本的情况下工作,并且仍然接受散列作为输入。 (我认为如果您特别关心Data::Dumper 的工作方式,我认为可以自定义它,但我以前没有这样做过)。
  • 您是否尝试在 C++ 中删除函数参数的 const

标签: perl swig


【解决方案1】:

你试过了吗:

print Dumper stl::TryMap(('a' => '4'));

【讨论】:

  • 为什么是-1?好的,我不熟悉 SWIG 界面,但我提供了一个语法建议。
【解决方案2】:

我将您的 C++ 函数作为内联函数放入头文件中进行测试。

然后我能够构建一个 SWIG 界面来满足您的需求。它有两个关键部分。首先,我编写了一个类型映射,它允许将 std::map perl 哈希作为输入给需要 std::map 的 C++ 函数。在后者的情况下,它从 perl 哈希构建一个临时映射以用作参数。 (这很方便但可能很慢)。类型图通过检查实际传入的内容来选择正确的行为。

解决方案的第二部分是将一些 C++ map 的成员函数映射到 perl 用于重载哈希操作的特殊函数。其中大部分是用%rename 简单实现的,其中C++ 函数和perl 函数是兼容的,但是FIRSTKEYNEXTKEY 不能很好地映射到C++ 的迭代器上,所以这些是使用%extend 和(内部)另一个@ 实现的987654328@ 存储我们正在包装的地图的迭代状态。

这里没有实现用于返回映射的特殊类型映射,但是通过现在实现的特殊操作有额外的行为。

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

理论上,您可以编写一个输出类型映射来在每个函数调用返回时自动设置此绑定,但到目前为止,我还没有成功编写一个可与​​绑定和 SWIG 运行时类型系统一起使用的类型映射。

应该注意,这不是生产就绪代码。据我所知,内部映射存在线程安全问题,并且也缺少一些错误处理。除了您在上面看到的之外,我还没有完全测试 perl 方面的所有哈希操作工作。通过与swig_map_common 宏交互,使其更通用也很好。最后,我无论如何都不是 perl 专家,而且我没有过多地使用 C API,所以在这方面需要谨慎。

【讨论】:

猜你喜欢
  • 2017-08-08
  • 2011-05-29
  • 2016-07-25
  • 1970-01-01
  • 2019-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
相关资源
最近更新 更多