【问题标题】:Wrapper generator SWIG (C++/Perl): How to access "blessed" objects in a 1d vector<double>in Perl?包装器生成器 SWIG (C++/Perl):如何在 Perl 中访问一维向量中的“祝福”对象?
【发布时间】:2022-01-15 00:06:19
【问题描述】:

我编写了一个 C++ 库来从电子设计自动化 (EDA) 工具中提取仿真数据(= 带有 (x,y) 或 (x,y,z) 分量的简单向量)。更具体地说,这些数据代表不同时间点的电信号。

C++ 库提供了多种方法。两个重要的是:

std::vector<std::string> getSignalNames() // Returns all signal names in the file
std::vector<std::vector<double>> getSignals() // Returns the actual data as M x N matrix (with M rows and N columns)

在 C++ 中使用该库可以完美运行并产生预期结果,例如:

getSignalNames():
  Signal1
  Signal2
getSignals():
  1 1 1 2
  2 1 2 3

Perl 程序员要求我也向他们提供该库,我决定使用the wrapper generator SWIG 来创建绑定。我通过the tutorial 工作,并且能够成功设置一个最小的工作示例。

基于示例,我为 C++ 库编写了完整的 SWIG 接口文件。包装器生成和构建过程运行顺利,我也可以毫无问题地使用getSignalNames()

// Perl snippet to read out signal names
my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalNames = $parserPointer->getSignalNames();

foreach my $signalName ( @$signalNames ) {
    print "$signalName\n";
}
// Output:
Signal1
Signal2

但是,我在使用getSignals()的返回值时遇到了麻烦:

// Perl snippet to read out the actual signal data
my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalData = $parserPointer->getSignals();

foreach my $rowAsHashRef ( @$signalData ) {
    print "reftype: " . reftype($rowAsHashRef) . "\n";
    print "keys: " . keys(%$rowAsHashRef) . "\n"
}
// Output:
reftype: HASH
keys: 0
reftype: HASH
keys: 0

如您所见,在 Perl 中每一行都表示为哈希,但哈希中没有键。不过,当使用Perl's Data::Dumper 时,我可以看到每一行的正确数据类型:

my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalData = $parserPointer->getSignals();
print Dumper $signalData;
// Output:
$VAR1 = [
          bless( {}, 'waveformparser::vector_1d_double' ),
          bless( {}, 'waveformparser::vector_1d_double' )
];

即,根据数据转储器,每行包含几列(即'waveformparser::vector_1d_double'),这些列在 SWIG 接口文件中定义如下:

...
%include "std_vector.i"
%template(vector_1d_double) std::vector<double>;
%template(vector_2d_double) std::vector<std::vector<double>>;
...

我现在的问题是:如何在 Perl 中访问这个“祝福”(包装)vector_1d_double 对象的元素?

我想,SWIG 会为这些对象提供方便的访问方法。即,底层 C++ 数据类型只是一个简单的双精度一维向量 (std::vector&lt;double&gt;)。

【问题讨论】:

  • 您可能需要为std::vector&lt;std::vector&lt;double&gt;&gt; 编写一个类型映射,类似于为std::map&lt;...&gt; here 所做的工作
  • 您不必编写包含std_vector.i 的类型图。您应该包含minimal reproducible example 以查看您的 .i 文件中是否有任何问题。例如,模板必须在通过%include 处理代码头之前实例化。

标签: c++ perl vector double swig


【解决方案1】:

您需要为std::vector&lt;std::vector&lt;double&gt;&gt; 编写一个输出类型映射以转换为适当的Perl 数组数组。这是一个例子:

VecVec.i

%module VecVec
%typemap(out) std::vector<std::vector<double>> {
    AV *array = (AV *) newAV();
    auto vec_ptr = &$1;
    std::vector<std::vector<double>>& vec = *vec_ptr;
    for (const auto &item : vec) {
        AV *subarray = (AV *) newAV();
        for (const auto &subitem : item) {
            av_push(subarray, newSVnv((NV) subitem));
        }
        av_push(array, (SV*) newRV_noinc((SV*)subarray));
    }
    $result = newRV_noinc((SV*) array);
    sv_2mortal($result);
    argvi++;
}
%{
    #include "VecVec.h"

%}
%include "VecVec.h"

VecVec.h

#ifndef VEVEC_H_
#define VECVEC_H_
#include <vector>

class VecVec
{
public:
    VecVec() {}
    ~VecVec() {}
    std::vector<std::vector<double>> getSignals();
private:
};
#endif

VecVec.cpp

#include <string>
#include <vector>
#include <iostream>
#include "VecVec.h"

std::vector<std::vector<double>> VecVec::getSignals()
{
    std::vector<std::vector<double>> vec {
        {1, 2, 3},
        {4, 5, 6}
    };
    return vec;
}

然后编译:

perl_include_dir=$(perl -MConfig -e'print $Config{archlib}')"/CORE"
swig -perl5 -c++ -I/usr/include VecVec.i
g++ -fPIC -c VecVec.cpp
g++ -I${perl_include_dir} -c -fPIC -g -o VecVec_wrap.o VecVec_wrap.cxx
g++ -shared -L. VecVec.o VecVec_wrap.o -o VecVec.so

并使用 test.pl 测试模块:

use strict;
use warnings;
use Data::Dumper qw(Dumper);
use lib '.';
use VecVec;

my $p = VecVec::VecVec->new();
my $sig = $p->getSignals();
print Dumper($sig);

输出

$VAR1 = [
          [
            '1',
            '2',
            '3'
          ],
          [
            '4',
            '5',
            '6'
          ]
        ];

【讨论】:

  • 非常非常感谢您的清晰解释和工作代码 - 完美。你让我今天一整天都感觉很好! :) %typemap(out) std::vector&lt;std::vector&lt;double&gt;&gt; {...} 定义是 SWIG 接口文件 (*.i) 中缺失的谜题。我认为 SWIG 会在使用 SWIG 的 %template(...) 指令时立即生成这个。
【解决方案2】:

回复:

我想,SWIG 会为这些对象提供方便的访问方法。即,底层 C++ 数据类型只是一个简单的双精度一维向量 (std::vector)。

应该的。您确实需要在 SWIG 处理函数之前实例化模板,但您需要展示一个最小的、可重现的示例来确定它为什么不起作用。

我目前不熟悉如何构建 Perl 扩展,但这里有一个适用于 Perl 的非特定语言 SWIG .i 文件。我将使用 Python 进行演示:

test.i

%module test

// Define templates before SWIG processes the code.
%include "std_vector.i"
%include "std_string.i"
%template(vector_string) std::vector<std::string>;
%template(vector_1d_double) std::vector<double>;
%template(vector_2d_double) std::vector<std::vector<double>>;

%inline %{
#include <vector>
#include <string>

std::vector<std::string> getSignalNames() {
    return {"abc","123"};
}

std::vector<std::vector<double>> getSignals() {
    return {{1.1,2.2},{3.3,4.4}};
}
%}

以上内容包括独立文件中的所有内容。我使用 swig -c++ -python test.i 并将生成的 test_wrap.cxx 文件编译为 Python 扩展。

演示:

>>> import test
>>> test.getSignalNames()
('abc', '123')
>>> test.getSignals()
((1.1, 2.2), (3.3, 4.4))

如果 SWIG 的 Perl 支持具有可比性,则此 .i 文件应该适合您,而无需定义您自己的输出类型映射。

【讨论】:

  • 嗨,马克,感谢您的努力。不幸的是,在创建 Perl 绑定并像这样使用它们时,您的最小工作示例也没有产生预期的输出:print Dumper(test::getSignals()); # Output: $VAR1 = [ bless( {}, 'test::vector_1d_double' ), bless( {}, 'test::vector_1d_double' ) ]; 我不知道为什么,但是在生成 Perl 绑定时,SWIG 需要一个额外的类型映射来为Håkon Hægland 指出的 C++ vector&lt;vector&lt;double&gt;&gt;
  • 另见此公开票:github.com/swig/swig/issues/566
猜你喜欢
  • 2011-01-20
  • 2014-01-19
  • 1970-01-01
  • 2011-05-04
  • 2011-07-26
  • 2013-05-04
  • 2011-11-14
  • 1970-01-01
  • 2015-11-08
相关资源
最近更新 更多