Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 399
RobW Avatar asked Jul 15 '11 15:07

RobW


People also ask

How to use wrapping C/C++ for Python using SWIG?

Wrapping C/C++ for Python using SWIG 1 Installation. ... 2 My c file is example.c 3 Interface file: Now, if you want to add you c file to your preferred language, you need to write an “interface file” which is the input to SWIG. 4 example.i. ... 5 Header file. ... 6 example.h 7 Setup file: 8 Creating the wrapper. ... 9 Built the extension. ...

Why does Swig unify all types of objects in Python?

In Python, there is no detailed distinction like this--specifically, there are only "objects". There are no pointers, references, arrays, and so forth. Because of this, SWIG unifies all of these types together in the wrapper code. For instance, if you actually had the above functions, it is perfectly legal to do this:

How do you use a wrapper function in C++?

The C wrapper function compares the pointer to the Python object that called the wrapper function to the pointer stored by the director. If these are the same, then the C wrapper function tells the director to resolve the method by calling up the C++ inheritance chain, preventing an infinite loop.

What is Swig in C++?

What is SWIG In a nutshell, SWIG is a compiler that takes C/C++ declarations and creates a wrapper needed to access those declarations from other languages like Python, Tcl, Ruby etc. It normally required no changes in existing code and create an interface within a minute. Reasons for creating wrapper


2 Answers

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
like image 151
Chris Card Avatar answered Sep 24 '22 15:09

Chris Card


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...

like image 31
RobW Avatar answered Sep 22 '22 15:09

RobW