Home > OS >  Wrapper generator SWIG (C /Perl): How to access "blessed" objects in a 1d vector<doubl
Wrapper generator SWIG (C /Perl): How to access "blessed" objects in a 1d vector<doubl

Time:12-10

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'
          ]
        ];
  • Related