包装器生成器 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 文件应该适合您,而无需定义您自己的输出类型映射。
我编写了一个 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 文件应该适合您,而无需定义您自己的输出类型映射。