Having completed a tour of the extension process, let’s zoom in for a closer look at SWIG’s compendium of features. We mentioned earlier that SWIG handles a useful subset of ANSI C/C++, which means support for data structures as well as functions. Specifically, it supports the following:
A C variable can be exported into Perl space as a scalar variable of the same name. SWIG supports the fundamental C data types, enums, and #defined constant values. Variables of complex or user-defined types are automatically mapped to a pair of get/set accessor functions.
Every pointer is treated as a
void
*
by default, regardless
of whether it is a char**
or
Matrix*
or double
***
. This strategy works especially well for
user-defined types, because most C libraries don’t expect you
to dereference such pointers. For example, fopen
returns a FILE *
, which is simply handed over to
fread()
and fwrite()
. In Perl,
this pointer is available as a scalar, and Perl doesn’t have to
know whether the pointer refers to an array, structure, or a typedef.
On the other hand, if you want a Vector
*
to a list of integer-valued scalars, you will
have to help SWIG out by providing a typemap.
Not every data type is a simple conversion from Perl to C or vice versa. SWIG (like xsubpp) provides a way for you to write arbitrary transformations, such as converting a Perl array to a 10-by-10 matrix. To write a typemap, you need to know Perl’s API for accessing its internal data types, so we’ll cover this topic in Section 20.5.7 in Chapter 20. Typemaps can be applied not just to function parameters, but also to structure members and global variables. You can also optionally create named typemaps, which apply to specific named entities (function arguments, variable names, function names), instead of all entities of that type.
Both simple arrays (vector[100])
and
multidimensional arrays (vector[10][10]
) are
mapped to a simple pointer (vector
*
). Typemap support for arrays exists, but there
are still a number of thorny issues for which SWIG cannot provide a
general solution; please read the SWIG documentation for details.
SWIG automatically creates accessor functions for each member of a structure or class defined in the interface file. As with the other facilities, these declarations cannot have the full generality of a C structure or a C++ class, but they are powerful enough for handling most common interface issues.
SWIG
provides constructor and destructor procedures, which allow you to
allocate and free C structures from Perl space. You can convert basic
C structures to objects in Perl space with a primitive called
%addmethods
.
SWIG creates function wrappers that look pretty similar to their C equivalents. Each parameter can be optionally typemapped, but since a typemap provides a translation in isolation (from other parameters), the number of parameters cannot be changed. This is not a constraint in XS.
In other words, with SWIG you cannot map the C function
char ** permute(char *string); // returns permutations of string
to
@array = permute ($str);
because one parameter, char**
, needs conversion to
a variable number of scalars (to be assigned to
@array
). You can instead write a typemap to
convert the char**
to an array and
return its reference, so in Perl space, it is
accessible this way:
$rarray = permute ($str); print join(' ', @$rarray);
Of course, you can always write a wrapper Perl function and insert it
in the .pm
file created automatically by SWIG:
sub fancy_permute { @{permute($_[0])}; # dereferences array }
Parameters can have default values but, as in C++, can be applied only to the rightmost parameters. This is how you specify the function signature in the interface file:
draw_mandel (file,width,height,orig_real,orig_imag,range,depth=30);
This allows you to optionally skip the last parameter when calling from Perl.
SWIG provides a %except
directive to wrap all external library calls inside a generic
exception handler. This way you can trap all user-defined errors and
C++ exceptions in one central place and translate them into Perl
exceptions. Please see the SWIG documentation for examples.
SWIG optionally creates wrapper Perl code
that allows you to access member attributes and functions of C or C++
objects using the Perl hash notation,
$person->{age}
. This mechanism is built on top
of the attribute accessor functions mentioned earlier.
An embedded structure gets the same treatment as an outermost structure—accessor functions and support from shadow classes.
The following interface file shows an example of using classes, accessing methods, and creating shadow classes:
%module Graphics class Shape { public: int x, y; // origin int w, h; // width, ht (defines bounding area) draw(); }; class Polygon : public Shape { public: Polygon(int x, int y, int w, int h); draw(); };
We invoke SWIG with the -c++
option, since it is
not enabled by default, and the -shadow
option for
creating shadow classes:
% swig -c++ -shadow Graphics.i
SWIG sets up an identical inheritance hierarchy in script space, and using this class in Perl feels completely natural:
use Graphics; $poly = new Polygon(10, 10, 30, 40); printf "Origin: %d %d ", $poly->{x}, poly->{y}; $poly->draw();
You’ll be happy to know that SWIG properly handles the relationship between base classes and derived classes. For example, a function involving a base class will recognize pointers that have been blessed into a derived class. In the case of multiple inheritance, SWIG performs proper C++ type-casting to make sure the pointer values are correct. XS has no such feature.
While the shadow class feature is convenient, you should be aware
that for every instance generated using new
, an
additional object is created internally. The reason is that to
support the member access notation
($poly->{x}
), new
returns a
tied hash, whose FETCH
subroutine calls the
appropriate accessor function. You know by now that the tie facility
interposes an intermediate object.