22.10. Wrapping C++ Classes with SWIGOne of the more clever tricks SWIG can perform is class wrapper generation. Given a C++ class declaration and special command-line settings, SWIG generates the following:
As before, to use SWIG in this domain, write and debug your class as though it would be used only from C++. Then, simply run SWIG in your makefile to scan the C++ class declaration and compile and link its output. The end result is that by importing the shadow class in your Python scripts, you can utilize C++ classes as though they were really coded in Python. Not only can Python programs make and use instances of the C++ class, they can also customize it by subclassing the generated shadow class. 22.10.1. A Simple C++ Extension ClassTo see how this works, we need a C++ class. To illustrate, let's code a simple one to be used in Python scripts.[*] The following C++ files define a Number class with three methods (add, sub, display), a data member (data), and a constructor and destructor. Example 22-18 shows the header file.
Example 22-18. PP3E\Integrate\Extend\Swig\Shadow\number.h
Example 22-19 is the C++ class's implementation file; each method prints a message when called to trace class operations. Example 22-19. PP3E\Integrate\Extend\Swig\Shadow\number.cxx
Just so that you can compare languages, the following is how this class is used in a C++ program. Example 22-20 makes a Number object, calls its methods, and fetches and sets its data attribute directly (C++ distinguishes between "members" and "methods," while they're usually both called "attributes" in Python). Example 22-20. PP3E\Integrate\Extend\Swig\Shadow\main.cxx
You can use the g++ command-line C++ compiler program to compile and run this code on Cygwin (it's the same on Linux). If you don't use a similar system, you'll have to extrapolate (there are far too many C++ compiler differences to list here). Type the compile command directly or use the cxxtest target in this directory's makefile, and then run the purely C++ program created: .../PP3E/Integrate/Extend/Swig/Shadow$ make -f makefile.number-swig cxxtest g++ main.cxx number.cxx -Wno-deprecated .../PP3E/Integrate/Extend/Swig/Shadow$ ./a.exe Number: 1 add 4 Number = 5 sub 2 Number = 3 Square = 9 99 Number = 99 0x4a0248 ~Number: 99 22.10.2. Wrapping the C++ Class with SWIGLet's get back to Python. To use the C++ Number class of the preceding section in Python scripts, you need to code or generate a glue logic layer between the two languages, just as in prior C extension examples. To generate that layer automatically, write a SWIG input file like the one shown in Example 22-21. Example 22-21. PP3E\Integrate\Extend\Swig\Shadow\number.i
This interface file simply directs SWIG to read the C++ class's type signature information from the included number.h header file. SWIG uses the class declaration to generate two different Python modules again:
The former must be compiled into a binary library. The latter imports and uses the former and is the file that Python scripts ultimately import. As for simple functions, SWIG achieves the integration with a combination of Python and C++ code. After running SWIG, the Cygwin makefile shown in Example 22-22 combines the generated number_wrap.cxx C++ wrapper code module with the C++ class implementation file to create a _number.dlla dynamically loaded extension module that must be in a directory on your Python module search path when imported from a Python script, along with the generated number.py. As before, the compiled C extension module must be named with a leading underscore in SWIG today: _number.dll, rather than the numberc.dll format used by earlier releases. The shadow class module number.py internally imports _number.dll. Example 22-22. PP3E\Integrate\Extend\Swig\Shadow\makefile.number-swig
As usual, run this makefile to generate and compile the necessary glue code into an extension module that can be imported by Python programs: .../PP3E/Integrate/Extend/Swig/Shadow$ make -f makefile.number-swig swig -c++ -python -shadow number.i g++ number_wrap.cxx -c -g -I/usr/include/python2.4 g++ -c -g number.cxx -Wno-deprecated g++ -shared number_wrap.o number.o -L/usr/bin -lpython2.4 -o _number.dll 22.10.3. Using the C++ Class in PythonOnce the glue code is generated and compiled, Python scripts can access the C++ class as though it were coded in Python. In fact, it isthe shadow class on top of the extension module is generated Python code. Example 22-23 repeats the main.cxx file's class tests; here, though, the C++ class is being utilized from the Python programming language. Example 22-23. PP3E\Integrate\Extend\Swig\Shadow\main.py
Because the C++ class and its wrappers are automatically loaded when imported by the number shadow class, you run this script like any other: .../PP3E/Integrate/Extend/Swig/Shadow$ python main.py Number: 1 add 4 Number = 5 sub 2 Number = 3 Square = 9 99 Number = 99 <number.Number; proxy of C++ Number instance at _b0974700_p_Number> ~Number: 99 This output is mostly coming from the C++ class's methods and is the same as the main.cxx results shown in Example 22-20 (less the instance output format; it's a Python shadow class instance now). 22.10.3.1. Using the low-level extension moduleIf you really want to use the generated accessor functions module, you can, as shown in Example 22-24. This version runs the C++ extension module directly without the shadow class, to demonstrate how the shadow class maps calls back to C++. Example 22-24. PP3E\Integrate\Extend\Swig\Shadow\main_low.py
This script generates essentially the same output as main.py, though the C++ class instance is something lower level than the proxy class here: .../PP3E/Integrate/Extend/Swig/Shadow$ python main_low.py Number: 1 add 4 Number = 5 sub 2 Number = 3 Square = 9 99 Number = 99 <Swig Object at _400d4900_p_Number> ~Number: 99 22.10.3.2. Subclassing the C++ class in PythonUsing the extension module directly works, but there is no obvious advantage to moving from the shadow class to functions here. By using the shadow class, you get both an object-based interface to C++ and a customizable Python object. For instance, the Python module shown in Example 22-25 extends the C++ class, adding an extra print statement to the C++ add method and defining a brand-new mul method. Because the shadow class is pure Python, this works naturally. Example 22-25. PP3E\Integrate\Extend\Swig\Shadow\main_subclass.py
Now we get extra messages out of add calls, and mul changes the C++ class's data member automatically when it assigns self.dataPython code extends C++ code: .../PP3E/Integrate/Extend/Swig/Shadow$ python main_subclass.py Number: 1 in Python add... add 4 Number = 5 sub 2 Number = 3 Square = 9 99 Number = 99 in Python mul... Number = 198 <_ _main_ _.MyNumber; proxy of C++ Number instance at _580d4900_p_Number> ~Number: 198 In other words, SWIG makes it easy to use C++ class libraries as base classes in your Python scripts. Among other things, this allows us to leverage existing C++ class libraries in Python scripts and optimize by coding parts of class hierarchies in C++ when needed. 22.10.3.3. Exploring the wrappers interactivelyAs usual, you can import the C++ class interactively to experiment with it some more: .../PP3E/Integrate/Extend/Swig/Shadow$ python >>> import _number >>> _number._ _file_ _ # the C++ class plus generated glue module '_number.dll' >>> import number # the generated Python shadow class module >>> number._ _file_ _ 'number.pyc' >>> x = number.Number(2) # make a C++ class instance in Python Number: 2 >>> y = number.Number(4) # make another C++ object Number: 4 >>> x, y (<number.Number; proxy of C++ Number instance at _a0764900_p_Number>, <number.Number; proxy of C++ Number instance at _508b4900_p_Number>) >>> x.display( ) # call C++ method (like C++ x->display( )) Number = 2 >>> x.add(y.data) # fetch C++ data member, call C++ method add 4 >>> x.display( ) Number = 6 >>> y.data = x.data + y.data + 32 # set C++ data member >>> y.display( ) # y records the C++ this pointer Number = 42 >>> y.square( ) # method with return value Square = 1764 >>> t = y.square( ) Square = >>> >>> t, type(t) (1764, <type 'int'>) Naturally, this example uses a small C++ class to underscore the basics, but even at this level, the seamlessness of the Python-to-C++ integration we get from SWIG is astonishing. Python code uses C++ members and methods as though they are Python code. Moreover, this integration transparency still applies once we step up to more realistic C++ class libraries. So what's the catch? Nothing much, really, but if you start using SWIG in earnest, the biggest downside may be that SWIG cannot handle every feature of C++ today. If your classes use some esoteric C++ tools (and there are many), you may need to handcode simplified class type declarations for SWIG instead of running SWIG over the original class header files. SWIG development is ongoing, so you should consult the SWIG manuals and web site for more details on these and other topics. In return for any such trade-offs, though, SWIG can completely obviate the need to code glue layers to access C and C++ libraries from Python scripts. If you have ever coded such layers by hand in the past, you already know that this is a very big win. If you do go the handcoded route, though, consult Python's standard extension manuals for more details on both API calls used in this and the next chapter, as well as additional extension tools we don't have space to cover in this text. C extensions can run the gamut from short SWIG input files to code that is staunchly wedded to the internals of the Python interpreter; as a rule of thumb, the former survives the ravages of time much better than the latter. |