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:
-
Constants and global variables
-
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.
-
Pointers
-
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.
-
Typemaps
-
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 the section
"SWIG Typemaps"
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.
-
Arrays
-
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.
-
Structures and C++ classes
-
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.
-
Methods
-
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
.
-
Ordinary functions
-
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
}
-
Default and optional parameters
-
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.
-
Centralized exception handling
-
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.
-
Shadow classes
-
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.
-
Nested structures
-
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 \n", $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.
|
|