As mentioned in the Steps in integration of a component chapter, the internal object built in the Defining an internal object chapter can be manipulated from a local python interpreter according to the following scheme.
Access from a local python interpreter
In the case of a C++ internal object, a python/ C++ interface has to be written to obtain a local component. The next section describes how this interface is written. Nothing needs to be done in the case of a python internal object: the python internal object can be used as a local component.
There is no need to introduce an additional interface if the internal object is implemented as a python object.
A python/C++ interface has to be used before a C++ object can be used from a python interpreter. This interface can be coded by the integrator or it can be generated (semi-) automatically using tools such as swig [SWIG] or boost [BOOST]. This document describes how the interface is generated using swig, through a simple example. Refer to the swig documentation, or even the python documentation, for processing of special cases.
The standard procedure to use swig is to write an interface file (terminating with .i). This interface file is very similar to a C++ interface file (for example see vecteur.hxx or FreeFem.hxx). It contains all C++ declarations (structures, functions, classes, constants, etc.) that the integrator wants to “export” to the python level. Only the public part of classes can be indicated in the interface file for C++ classes. Examples will be given later.
Therefore, all components to be integrated will be compiled in the form of a dynamic library, which will mean a particular procedure for the use of debugging tools (see below). The various operations to be carried out and the files involved in the process are shown diagrammatically on the following figure.
Interface through swig
If it is required to access the alglin class from a local python interpreter, an interface file will be written with type:
alglin.i
%module AlgLinModule
%{
#include "alglin.hxx"
%}
class alglin {
public:
alglin();
~alglin();
void addvec(vecteur *C, vecteur *A, vecteur *B);
double prdscl(vecteur *A, vecteur *B);
vecteur * create_vector(long n);
void destroy_vector(vecteur *V);
};
The different lines mean:
%module AlgLinModule
Defines the name of the python module. We will write import AlgLinModule, to import the definitions of the component from a python interpreter.
%{
#include "alglin.hxx"
%}
The C++ declarations that the C++ / python interface code will need will have to be written between the %{ and %} lines (otherwise the C++ file generated by swig will not compile). Typically, the interface file of the C++ internal object constructed in the previous chapter will be included here.
class alglin {
public:
alglin ();
~alglin ();
void addvec (vecteur *C, vecteur *A, vecteur *B);
double prdscl (vecteur *A, vecteur *B);
vecteur * create_vector (long n);
void destroy_vector (vecteur *V);
};
The remainder of the alglin.i. file includes an indication about the classes and definitions that are to be exported to the python interpreter. Example use of the generated interface:
>>> import AlgLinModule
>>> C = AlgLinModule.alglin()
>>> C
<C alglin instance>
>>> v1 = C.create_vector(10)
>>> v2 = C.create_vector(20)
>>> print "v1 = ", v1
v1 = _8116410_vecteur_p
>>> print "v2 = ", v2
v2 = _80d06c8_vecteur_p
Notes
- A constructor (alglin() ) and a destructor (~alglin() ) have been introduced that were not in the declaration of the C++ class (file alglin.hxx). This constructor and this destructor are not necessary in the C++ class of the internal object (the internal object does not need to be initialised when it is created and does not manage the C++ dynamic memory). In this case, the compiler provides a default constructor and destructor. On the other hand, a constructor and a destructor must be explicitly declared for the swig interface file so that python can manage the C++ memory correctly (i.e. the internal C++ object is also created / deleted “cleanly” when a python object is created / deleted).
- Note that the definition of the vector structure/class is not explicitely described in the alglin.i interface file. Vector type objects will be seen from the python interpreter as “black box” objects (their type and memory location are known, but associated methods / attributes are not known). The following error message will be produced if an attempt is made to call a method on a vector object:
>>>> print v.n()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: n
The second version of this example (below) will correct this problem.
The first version of the example suffers from two defects (among others) concerning vector type objects:
Swig enriches the alglin.i interface file to add missing functions:
alglin.i (version 2)
%module AlgLinModule
%{
#include "alglin.hxx"
%}
class vecteur {
public:
vecteur(long n);
~vecteur();
double * x();
long n();
};
class alglin {
public:
alglin();
~alglin();
void addvec(vecteur *C, vecteur *A, vecteur *B);
double prdscl(vecteur *A, vecteur *B);
vecteur * create_vector(long n);
void destroy_vector(vecteur *V);
};
>>> import AlgLinModule
>>> n = 5
>>> C = AlgLinModule.alglin()
>>> v = C.create_vector(n)
>>> print "v =", v
v = <C vecteur instance>
>>> print "v.n =", v.n()
v.n = 5
>>> x = v.x()
>>> print x
_811a160_double_p
>>> print x[4]
a
>>> print type(x)
<type 'string'>
The second version of the example makes it possible to “see” vector type objects but only at the “surface”. In particular, there is no individual access to coefficients from the python interpreter. By adding utility functions (__setitem__ and __getitem__) into the alglin.i interface, the third version makes it possible to (partially) simulate genuine coefficient vectors from the python layer.
Note: We have also added an __str__ display function that displays the list of coefficients of v, when print v is executed from the interpreter.
alglin.i (version 3)
%module AlgLinModule
%{
#include "alglin.hxx"
#include <string>
static string tampon;
%}
class vecteur {
public:
vecteur(long n);
~vecteur();
double * x();
long n();
%addmethods {
double __getitem__(long i)
{ double *x_ = self->x(); return x_[i]; }
double __setitem__(long i, double value)
{ double *x_ = self->x(); x_[i] = value; }
const char * __str__() {
tampon = "";
double *x_ = self->x();
long n_ = self->n();
char sx[20];
for (long i=0; i<n_; i++) {
sprintf(sx, " %10.4g", x_[i]);
tampon += sx;
}
return tampon.c_str();
}
}
};
class alglin {
public:
alglin();
~alglin();
void addvec(vecteur *C, vecteur *A, vecteur *B);
double prdscl(vecteur *A, vecteur *B);
vecteur * create_vector(long n);
void destroy_vector(vecteur *V);
};
The following contains an example use of the component (including access to vectors):
>>> import AlgLinModule
>>> n = 5
>>> C = AlgLinModule.alglin()
>>> C
<C alglin instance>
>>> v1 = C.create_vector(n)
>>> v2 = C.create_vector(n)
>>> v1
<C vecteur instance>
>>> for i in range(n):
... v1[i] = 2*i
... v2[i] = i*i
...
>>> print "v1 =", v1
v1 = 0 2 4 6 8
>>> print "v2 =", v2
v2 = 0 1 4 9 16
>>> print v1[1]
2
>>> S = C.prdscl(v1, v2)
>>> print "S = ", S
S = 200.0