SWIG C++ Python: wrapping int by reference or pointer

10,053

Solution 1

I don't think python has the concept of return by reference, but here is my solution:

Blaat.i:

%module Blaat
%include typemaps.i
%apply int &OUTPUT { int & fA };
%{
/* Includes the header in the wrapper code */
#include "Blaat.hpp"
%}

/* Parse the header file to generate wrappers */
class Blaat
{
public:
 Blaat();
 void getA(int & fA);
 void setA(const int fA);
 ~Blaat();
};

b.py:

from Blaat import *
a = Blaat()
b = int(1)
a.setA(b)
b = a.getA()

Running:

python b.py
[Blaat::setA] fA = 1
[Blaat::getA] fA = 63

Solution 2

Thanks Chris, this works! After a bit of more digging, it seems that the SWIG documentation is not complete.

SWIG type conversion using the typemaps.i library is described here. What I get from the example is that you HAVE to manually specify that you want an argument to be used as output (which means that the SWIG documentation on 'pointers and references' only holds for INPUT arguments only!).

For the simple example above, it is sufficient to just including the .hpp file and let SWIG handle everything automatically.

Blaat.i:

%module Blaat
%include typemaps.i
%apply int &OUTPUT { int & fA };
%{
#include "Blaat.hpp"
%}

%include "Blaat.i"

PS: The Blaat.cpp file couts the wrong value, it should of course cout mA instead of fA since fA is set after the cout...

Share:
10,053
RobW
Author by

RobW

Updated on July 20, 2022

Comments

  • RobW
    RobW almost 2 years

    I'm trying to wrap some C++ functions into a Python wrapper. For this, it seems SWIG is a nice and easy way.

    Wrapping works, but I get a problem when passing integers by reference or by pointer. Since Python cannot work with references, SWIG internally converts these to pointers.

    Some simple example code:

    Blaat.hpp :

    #ifndef __BLAAT_HPP__
    #define __BLAAT_HPP
    class Blaat
    {
    public:
     int mA;
     float mB;
    
    public:
     Blaat() {}
     void getA(int & fA);
     void setA(const int fA);
     ~Blaat() {}
    };
    
    #endif // __BLAAT_HPP__
    

    Blaat.cpp

    #include "Blaat.hpp"
    #include <iostream>
    
    void Blaat::getA(int & fA) {
     std::cout << "[Blaat::getA] fA = " << fA << std::endl;
     fA = mA;
    } 
    
    void Blaat::setA(const int fA) {
     std::cout << "[Blaat::setA] fA = " << fA << std::endl;
     mA = fA;
    }
    

    Blaat.i:

    %module Blaat
    %{
    /* Includes the header in the wrapper code */
    #include "Blaat.hpp"
    %}
    
    /* Parse the header file to generate wrappers */
    %include "Blaat.hpp"
    

    Than convert the code into a Python wrapper:

    #!/bin/sh
    swig -python -c++ -v $1.i 
    gcc -c $1_wrap.cxx -fPIC -I/usr/include/python2.6
    gcc -shared $1_wrap.o -o _$1<library_path> so -L. -l$1
    

    This all works fine. Now, I start Python and do:

    from Blaat import *
    a = Blaat()
    b = int(1)
    a.setA(b) <-- fine, calls setA() function fine
    a.getA(b) <-- does not work
    

    At the "getA()" call, the following error occurs:

    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
     File "Blaat.py", line 86, in getA
       def getA(self, *args): return _Blaat.Blaat_getA(self, *args)
    TypeError: in method 'Blaat_getA', argument 2 of type 'int &'
    

    Note that I get this problem both when passing the argument by reference and by pointer. Looking at the generated "Blaat_wrap.cxx" file, it stops at the actual type conversion:

    res2 = SWIG_ConvertPtr(obj1, &argp2, SWIGTYPE_p_int,  0 );
    if (!SWIG_IsOK(res2)) {
     SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Blaat_getA" "', argument " "2"" of type '" "int &""'"); 
    }
    

    This means that the function SWIG_ConvertPtr() fails, which is strange because it seems that the type it checks for is SWIGTYPE_p_int. From the "setA()" function, we see that the type conversion works (if passing by value).

    The SWIG documentation tells me):

    C++ references are supported, but SWIG transforms them back into pointers. For example, a declaration like this :

    class Foo { public: double bar(double &a); }

    has a low-level accessor

    double Foo_bar(Foo *obj, double *a) { obj->bar(*a); }

    Can someone throw in the thing I'm missing? I'm quite stuck at this point... Found this post, but this did not help either

  • RobW
    RobW almost 13 years
    Thanks for your answer Chris. Indeed, this is the most trivial solution, just returning by value. But according to SWIG it should be possible to use references... So, this is what I want! :)
  • RobW
    RobW almost 13 years