包装器生成器 SWIG (C++/Perl):如何在 Perl 中访问一维向量<double>中的"blessed"对象?

Wrapper generator SWIG (C++/Perl): How to access "blessed" objects in a 1d vector<double>in Perl?

我编写了一个 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 to create bindings. I worked through 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() 中的 return 值时遇到了麻烦:

// 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<double>).

您需要为 std::vector<std::vector<double>> 编写输出类型映射以转换为正确的 Perl 数组数组。这是一个例子:

VecVec.i:

%module VecVec
%typemap(out) std::vector<std::vector<double>> {
    AV *array = (AV *) newAV();
    auto vec_ptr = &;
    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'
          ]
        ];

回复:

I thought, SWIG would provide convenient access methods for such objects. I.e., the underlying C++ data type is just a simple 1d vector of doubles (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 文件应该适合您,而无需定义您自己的输出类型映射。