Introduction

SWIG is a great tool to generate wrappers very easily in various scripting languages of C/C++ libraries. But if you develop a C/C++ library and its wrapper at the same time, it has a significant flow: the necessity to create input files distinct from the source code of your library. The more extensive the library is, the biggest issue is to have to maintain virtually two sets of header files.

With PySwig, the source header files are annotated, allowing the automatic generation of the input files needed by Swig. With those input files, Swig can then create the sources of the wrapper. In this way, the maintenance effort is reduced to its minimum, since only one set of header files is needed.

Furthermore, PySwig can also take care of calling Swig to generate the wrapper. The result is a Python script, importing PySwig, and defining the operations to carry out. In the end, one just has to call this Python script to generate the Swig input files and call Swig to create the wrapper source files in one step.

Annotations

The basic principle is to add XML-like annotations in comments to various effects: to comment out a line, or a block of code; to include some lines in the input files used by Swig; to add automatically include files.

Add source include file

//<swig_inc/>

Automatically add the include file to the Swig input file. As an example of usage of this annotation, the result in the Swig input file would be:

//<swig_inc>
%{
#include "simple/object.h"
%}
//</swig_inc>

Comment out one line

//<swig_out/>

This annotation is to comment out one line of source code in the Swig input file. See the line below as an example one line in a header file of a library.

std::string &get_name();   //<swig_out/>   In C#, string are immutable

This same line will be transformed to the following line in the Swig input file:

//     std::string &get_name();    //<swig_out/>   In C#, string are immutable

Commenting out a block of code

Often a block of code needs to be commented out. It can be done in this way.

//<swig_out>
    int m_value = 0;
    …
//</swig_out>

The result in the Swig input file is:

/* //<swig_out>
    int m_value = 0;
    …
//</swig_out> */

Add code specific to Swig

Often, it’s required to add code specific to Swig to handle templates, typemaps, director, etc. It can be done as follow:

/*<swig>
%apply std::string &OUTPUT …
//</swig>*/

It will result in the following line in a Swig input file:

//<swig>
%apply std::string &OUTPUT …
//</swig>

To add only one line, a simpler form is also possible:

//<swig/> %apply std::string &OUTPUT …

In the Swig input file, the line will look like the following:

%apply std::string &OUTPUT …

Build script

As mentioned earlier, PySwig can also be used to automate the creation of the wrapper by directly calling Swig. See the examples in the git repo:

There are two Python classes to be aware of:

  • FileConfig — configuration for a set of annotated source headers (input dir, output dir, source list, include base path). Call set_script_path(__file__) so relative paths resolve correctly.

  • PySwig — orchestrates parsing annotations, writing Swig .hh interface files, and optionally invoking Swig. Use PySwig(parse_cli=False) when configuring everything from a script.

Typical workflow:

  1. Create one or more FileConfig instances and register them on PySwig.

  2. Set module name, output directories, typemaps, and includes.

  3. Call generate() to write Swig input files from annotated headers.

  4. Configure language options and call run_swig() to produce wrapper sources.

The lower-level Swig class locates the Swig executable and runs it with cwd= set to the output directory. ProcessSrcFile parses annotations in a single header; you rarely instantiate it directly.

For command-line use, pyswig accepts optional header paths via PySwig.set_input_files(); paths are resolved without changing the process working directory.