8

basically I am trying to have a tuple/list which contains a dictionary of different data types of values(float/int/bool/char/list) in python.

I am getting this from the following code:

(<f_p.Bunch; proxy of <Swig Object of type 'Bunch *' at 0x7f4954bdde10> >, <f_p.Bunch; proxy of <Swig Object of type 'Bunch *' at 0x7f4954bdde40> >, <f_p.Bunch; proxy of <Swig Object of type 'Bunch
*' at 0x7f495668be70> >, <f_p.Bunch; proxy of <Swig Object of type 'Bunch *' at 0x7f4952d09a50> >)

I want to get the output in this form:

({'I':1.0,'B':2.0, 'C':3.0, 'dert_':[1.2, 2.3, 3.4, 4.5, 5.6]})

I ain't able to deal with this class object pointer (bunch*) and couldn't find any solution to it. I looked on the net but couldn't found a working solution for my case.

f_p.cpp:

#include <iostream>
#include "f_p.h"
#define CPP_14 0

std::vector<Bunch*> form_p(const double *array, int x, int y)  {

    std::vector<Bunch*> v;
    Bunch *b1 = new Bunch(5);
    b1->set_I_B_C(1.0, 2.0, 3.0);
    b1->set_dert_({1.2, 2.3, 3.4, 4.5, 5.6});

    float *_dert = b1->get_dert_();
    for(int i=0; i<5; i++) {
        std::cout << _dert[i] << std::endl;
    }

    v.push_back(b1);
    v.push_back(b1); 
    v.push_back(b1); 
    v.push_back(b1); 

    return v;
}

f_p.h:

#ifndef F_P_H
#define f_P_H
#include <memory> 
#include <vector>
#include <memory> 
#include <algorithm>
#include <tuple>
#include <initializer_list>

class Bunch {
    private:
        unsigned int start;
        unsigned int end;
        float I;
        float B;
        float C;
        bool isPos;
        std::unique_ptr<float[]> dert_;
    public:

        explicit Bunch(size_t width) {
            #if CPP_14
            this->dert_ = std::make_unique<float[]>(width);
            #else
            this->dert_ = std::unique_ptr<float[]>(new float[width]);
            #endif
            std::fill_n(this->dert_.get(), width, -1.0);
        }

        void set_I_B_C(float I, float B, float C) {
            this->I = I;
            this->B = B;
            this->C = C;
        }

        std::tuple<float, float, float> get_I_B_C() const {
            return std::make_tuple(this->I, this->B, this->C);
        }

        float* get_dert_() const {
            return this->dert_.get();
        }

        void set_dert_(std::initializer_list<float> l) {
            int i = 0;
            for (auto e: l){
                dert_[i++] = e;
            }
        }
};
/* Define function prototype */
std::vector<Bunch*> form_p(const double *array, int x, int y)  ;
#endif

f_p.i:

%module f_p
#define SWIGPYTHON_BUILTIN

%{
  #include "numpy/arrayobject.h"
  #define SWIG_FILE_WITH_INIT  /* To import_array() below */
  #include "f_p.h"
%}
%include "std_map.i"
%import "std_deque.i" 
%import "std_vector.i" 

%template (mapiv) std::map<char,float>;
%template () std::vector<Bunch*>;
%include "numpy.i"

%init %{
import_array();
%}

%apply (double* IN_ARRAY2, int DIM1, int DIM2) {
  (const double* array, int x, int y)
}

%include "f_p.h"

build.sh:

rm *.o f_p_wrap.cpp _f_p.so f_p.py
rm -rf __pycache__

g++ -O3 -march=native -fPIC -c f_p.cpp
swig -python -c++ -o f_p_wrap.cpp f_p.i

# Next, compile the wrapper code:

g++ -O3 -march=native -w -fPIC -c $(pkg-config --cflags --libs python3) -I /home/antpc/anaconda3/lib/python3.7/site-packages/numpy/core/include f_p.cpp f_p_wrap.cpp

g++ -std=c++11 -O3 -march=native -shared f_p.o f_p_wrap.o -o _f_p.so -lm

test_sample.py:

from f_p import form_p
import numpy as np
x = np.random.randn(3, 4)
print(form_p(x))
ninjakx
  • 35
  • 13
  • I modified my code https://stackoverflow.com/questions/59712985/how-to-make-a-stdmap-to-hold-different-data-types-in-swig to work for different data types. BUt now not able to interface it. – ninjakx Jan 16 '20 at 08:40
  • 1
    It looks like you just want to create an `typemap(out)` for `Bunch`. Why does `form_p` return `std::vector` instead of `std::vector`? Swig isn't going to free those pointers so you're leaking memory. – Indiana Kernick Jan 22 '20 at 02:29
  • If an answer helped in solving your problem then don't let the bounty go to waste – Indiana Kernick Jan 26 '20 at 03:48
  • @Kerndog73: I wasn't aware it awards only 50% of the bounty when not chosen and rest is gone wasted. – ninjakx Jan 27 '20 at 06:55

1 Answers1

4

The question really boils down to this: you have a class and you'd like to convert it a native Python object (rather than a wrapped object). SWIG automatically generates wrapped objects but you have to go out of your way to convert C++ types to native Python types.


I'm assuming this simplified Bunch to make the typemap a little more readable. You should be able to adapt this to your Bunch quite easily. Alternatively, you could convert your class to this simple struct before passing it on to Python.

struct Bunch {
    float i, b, c;
    std::vector<float> dert;
};

Bunch makeBunch();

Here's the implementation of makeBunch.

Bunch makeBunch() {
    return {1.0, 2.0, 3.0, {1.2, 2.3, 3.4, 4.5, 5.6}};
}

I've omitted error checking to keep it short and concise.

%typemap(out) Bunch {
    $result = PyDict_New();
    PyDict_SetItem($result, PyBytes_FromString("I"), PyFloat_FromDouble($1.i));
    PyDict_SetItem($result, PyBytes_FromString("B"), PyFloat_FromDouble($1.b));
    PyDict_SetItem($result, PyBytes_FromString("C"), PyFloat_FromDouble($1.c));
    PyObject *dert = PyList_New($1.dert.size());
    for (size_t i = 0; i != $1.dert.size(); ++i) {
        PyList_SetItem(dert, i, PyFloat_FromDouble($1.dert[i]));
    }
    PyDict_SetItem($result, PyBytes_FromString("dert_"), dert);
}

When I compile and run this myself, I get the expected results (well, close enough!).

>>> import test
>>> test.makeBunch()
{'I': 1.0, 'C': 3.0, 'B': 2.0, 'dert_': [1.2000000476837158, 2.299999952316284, 3.4000000953674316, 4.5, 5.599999904632568]}

The typemap isn't strictly necessary. You can instead just use wrapped objects. Remove the type map and make sure to expose std::vector<float> like so.

%include "std_vector.i"
%template(FloatVector) std::vector<float>;

Then you can access Bunch via a wrapped object.

>>> import test
>>> bunch = test.makeBunch()
>>> bunch
<test.Bunch; proxy of <Swig Object of type 'Bunch *' at 0x10b6daba0> >
>>> bunch.i
1.0
>>> bunch.b
2.0
>>> bunch.c
3.0
>>> bunch.dert
<test.FloatVector; proxy of <Swig Object of type 'std::vector< float,std::allocator< float > > *' at 0x10b6dadb0> >
>>> for d in bunch.dert:
...     print(d)
...
1.20000004768
2.29999995232
3.40000009537
4.5
5.59999990463

Some reputable sources:

Indiana Kernick
  • 5,041
  • 2
  • 20
  • 50
  • Can you answer this question: https://stackoverflow.com/questions/59712985/how-to-make-a-stdmap-to-hold-different-data-types-in-swig the answer for that is also the same as this one. I want to close that question. – ninjakx Jan 27 '20 at 06:59
  • @ninjakx If you want to close that question then just close it (or delete it). – Indiana Kernick Jan 27 '20 at 07:38