I have written a C library to extract simulation data (= simple vectors with (x,y) or (x,y,z) components) from electronic design automation (EDA) tools. More concrete, this data represents electrical signals for different points in time.
The C library offers several methods. Two important ones are:
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)
Using the library in C works perfectly and yields the expected results, e.g.:
getSignalNames():
Signal1
Signal2
getSignals():
1 1 1 2
2 1 2 3
Perl programmers asked me to also offer the library to them and I decided to use the wrapper generator SWIG to create bindings. I worked through the tutorial and I was able to successfully set up a minimal working example.
Based on the example, I wrote a complete SWIG interface file for the C library. The wrapper generation and build process works smoothly and I can also use getSignalNames()
without any problems:
// 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
But, I ran into trouble when using the return value from 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
As you see, each row is represented as hash in Perl, but there are no keys in the Hash. Nevertheless, when using Perl's Data::Dumper, I can see the correct data type for each row:
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' )
];
I.e., according to the data dumper, each row consists of several columns (i.e., 'waveformparser::vector_1d_double'
) which are defined in the SWIG interface file as following:
...
%include "std_vector.i"
%template(vector_1d_double) std::vector<double>;
%template(vector_2d_double) std::vector<std::vector<double>>;
...
My question is now: How can I access elements of this "blessed" (wrapped) vector_1d_double
objects in Perl?
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<double>
).
CodePudding user response:
You need to write an output typemap for std::vector<std::vector<double>>
to convert to a proper Perl array of arrays. Here is an example:
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;
}
Then compile with:
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
and test the module with 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);
Output:
$VAR1 = [
[
'1',
'2',
'3'
],
[
'4',
'5',
'6'
]
];