Enough general talk! Let us test-drive both tools using a simple piece of fractal-drawing code. This problem is tailor-made for C, because generating a fractal image involves performing a series of computations on every pixel, which calls for compact data structures and fast number-crunching. This exercise creates the familiar Mandelbrot set image shown in Figure 18.3 .
Our Mandelbrot code is implemented in mandel.c and mandel.h . To avoid a non-portable GUI solution, we use a public domain library, gd , written by Tom Boutell [ 14 ], which allows you to treat a GIF file as a canvas and render points, lines, and circles on it. This GIF file can then be viewed by using any web browser.
mandel.c implements one function called draw_mandel , with the signature shown in Example 18.1 .
extern int draw_mandel (char *filename, int width, int height, double origin_real, double origin_imag, double range, double depth);
The meaning of the parameters will be explained in the Section 18.6, "A Detour into Fractals ," later in this chapter. First, we'll first concentrate on making it callable from Perl.
We start by writing a SWIG interface file, Fractal.i , as in Example 18.2 .
%module Fractal %{ #include "mandel.h" %} %include mandel.h
The %module statement gives a unique namespace to all the interface declarations in that file. We call the module Fractal because we would like to have one namespace for all fractal drawing code, and the Mandelbrot set is only one of many choices.
The statements between %{ and %} are meant for "raw" C code. We include mandel.h here because the interface file is soon going to be converted to C glue code, which in turn needs this header. Now comes the portion where all data structures and exported functions (with complete signatures) are to be listed. Since the interface file format is very close to ANSI C, we can simply %include mandel.h . Unlike the first include, which began with a # because it is called later from C code, this include starts with % because it is called immediately within SWIG.
Next, we invoke SWIG on this interface file and specify perl5 as the scripting language:
% swig -perl5 Fractal.i Generating wrappers for Perl 5 % ls mandel.h mandel.c Fractal_wrap.doc Fractal.i Fractal.pm Fractal_wrap.c
SWIG creates four files from the interface file. Fractal.pm contains some code to make the C library dynamically loadable. Fractal_wrap.c contains the wrapper code; for a function foo listed in the interface file, this wrapper file contains a function called _wrap_foo that translates Perl argument values to C, calls foo , and packages the return results back into Perl data types. You don't have to understand the contents of Fractal.pm and Fractal_wrap.c . SWIG also extracts all documentation out of the interface file into Fractal_wrap.doc (ASCII) , or Fractal_wrap.html (HTML), or Fractal_wrap.tex (LaTeX).
All we have left to do is to compile the two .c files and make them dynamically loadable.[ 3 ] SWIG (as well as XS) simplifies this part again by helping you create a makefile. Because a makefile is dependent on machine- and site-specific details such as operating system peculiarities, compiler, linker options, Perl installation directories, the name and location of the C compiler, and so on, these tools do not generate a makefile directly. Instead they generate a Perl script called Makefile.PL , which, when executed, produces a makefile that is customized for your system. This script is very simple, shown here after manually adding the LIBS and OBJECT lines:
use ExtUtils::MakeMaker; WriteMakefile( 'NAME' => 'Fractal', # Name of module 'LIBS' => [M # All custom libraries to be linked with 'OBJECT' => 'mandel.o Fractal_wrap.o' # All object files );
[3] Since this is a much simpler option than static linking, and since most self-respecting operating systems support it, we consider only dynamic linking in this book.
The standard ExtUtils::MakeMaker module does all the magic of finding out about the configuration of your system and creating a custom makefile.
The next three steps build and install this extension:
% perl Makefile.PL # create Makefile % make # compiles sources and creates shared library % make install # optional. installs library
(How much easier and more portable do you want it to get?)
We are now all set to create fractal images. The following call to draw_mandel() creates the beautiful image shown in Figure 18.3 .
use Fractal; Fractal::draw_mandel('mandel.gif', 300, 300, # file, width, height -1.5, 1.0, # origin x, y 2.0, 20); # range, max iterations
Since the chief purpose of this chapter is to illustrate writing extensions, we'll (reluctantly) put off the discussion of draw_mandel to the end.
The XS process is also extremely straightforward. h2xs understands normal C header files, so a fractal extension is produced as follows:
% h2xs -x -n Fractal mandel.h
This creates Fractal.pm , the Perl module, Makefile.PL , the makefile-generating script, and Fractal.xs . At this point, you don't need to know what this file contains.
Since Makefile.PL is automatically generated, you will need to add or modify the OBJECT and LIB lines, as shown earlier. The build and install are identical to what we saw earlier:
% perl Makefile.PL % make % make install
The makefile generated in the first step notices Fractal.xs , and feeds it to xsubpp to create the glue code in Fractal.c . Note that the name is not Fractal_wrap.c as with SWIG, so the OBJECT line in Makefile.PL should look like this:
'OBJECT' => 'mandel.o Fractal.o' # mandel.o contains the real function # Fractal.o contains the glue code