Advanced Perl Programming

Advanced Perl ProgrammingSearch this book
Previous: 18.2 Example: Fractals with PerlChapter 18
Extending Perl:A First Course
Next: 18.4 XS Features
 

18.3 SWIG Features

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.


Previous: 18.2 Example: Fractals with PerlAdvanced Perl ProgrammingNext: 18.4 XS Features
18.2 Example: Fractals with PerlBook Index18.4 XS Features