Simplified Wrapper and Interface Generator (SWIG)
By Mark Volkmann, OCI Partner
January 2006
Introduction
SWIG generates wrapper code from C/C++ header files that allows C/C++ functions to be invoked from other languages. This includes
- compiled languages such as C#, Java, Lua, Modula-3 and Ocaml
- "scripting languages" such as Perl, PHP, Pike, Python, Ruby and TCL
- LISP variants such as Allegro CL, CLISP
- Scheme variants such as CHICKEN, Guile and MzScheme
Why would you want to do such a thing? The most common reasons are
- Performance
The C/C++ implementation may be faster than what could be implemented in the calling language. - Code Reuse
There may not be enough time or desire to rewrite existing C/C++ code in the calling language.
Using SWIG reduces the amount of manual coding required to invoke C/C++ functions from other programming languages. In the case Java, JNI can be used directly, but this requires a large amount of tedious coding. Another benefit of using SWIG is that it is less error prone than manual coding. Correct use of JNI is complicated, especially for passing non-primitive types.
Setup
SWIG can be downloaded from http://www.swig.org.
Linux Setup
In some Linux distributions, SWIG is installed by default, making these setup steps unnecessary. If running "which swig
" fails to find it, the following steps will build it.
- unzip and untar the downloaded file
- cd to the directory created by the untar
./configure
make
su root
make install
Windows Setup
The Windows version of SWIG comes with a pre-built executable, so no installation is necessary. After unzipping, see Doc/Manual/Windows.html
for more information.
Example
Let's walk through an example of calling C++ from Java.
C++ header file - Person.h
- #ifndef PERSON_H
- #define PERSON_H
-
- // This example uses the Boost date_time library to
- // store dates and perform calculations using them.
- #include "boost/date_time/gregorian/gregorian.hpp"
- #include <string>
-
- class Person {
-
- public:
-
- Person(const std::string& name);
-
- // Call this before calling getAge,
- // getBirthday or isOlderThan.
- void setBirthday(const std::string& birthday);
-
- int getAge() const;
- boost::gregorian::date getBirthday() const;
- std::string getName() const;
- bool isOlderThan(const Person& person) const;
-
- private:
-
- boost::gregorian::date birthday_;
- std::string name_;
-
- };
-
- #endif
C++ source file - Person.cpp
- #include "Person.h"
-
- using namespace boost::gregorian;
- using namespace std;
-
- Person::Person(const string& name) : name_(name) {
- }
-
- int Person::getAge() const {
- date today(day_clock::local_day());
- int days1 = today.day_of_year();
- int days2 = birthday_.day_of_year();
-
- int years = today.year() - birthday_.year();
- if (days1 < days2) --years;
- return years;
- }
-
- date Person::getBirthday() const { return birthday_; }
-
- string Person::getName() const { return name_; }
-
- bool Person::isOlderThan(const Person& person) const {
- return birthday_ < person.birthday_;
- }
-
- void Person::setBirthday(const string& birthday) {
- birthday_ = from_string(birthday);
- }
SWIG input - Person.i
%module RubyPerson
%{
#include "Person.h"
%}
%include "std_string.i"
%include "Person.h"
Notes about the above file:
- SWIG directives start with "%".
- The module name is not used by Java, but is by some other languages such as Ruby.
- Everything between "%{" and "}%" is copied into the generated C++ wrapper code, Person_wrap.cxx in this case.
- std_string.i is needed to pass STL string objects as parameters.
- SWIG operates on the functions and class definitions contained in files referenced by "%include" directives.
- There are several SWIG directives that are not used in this example. Some are used for code insertion and others support documentation generation.
Running SWIG for Java
Here's a Unix/Linux script that runs SWIG to generate Java wrapper code that uses JNI.
package=com.ociweb.dates
# Create output directory.
packageWithSlashes=`echo $package | sed -e 's/\./\//g'`
outdir=gen/$packageWithSlashes
mkdir --parents $outdir
# Generate Java and C++ wrapper files.
# This processes Person.i.
swig -c++ -java -package $package -outdir $outdir *.i
# Compile wrapper code.
# JID stands for Java Include Directory.
jid=/usr/include/mozilla-1.7.8
gcc -c -fpic *.cpp *_wrap.cxx \
-I. -I$jid -I$jid/nspr -I/usr/local/include/boost_1_33_0
# Link wrapper code.
gcc -shared -fpic Person.o *_wrap.o -lstdc++ -o libPerson.so
Java application - Main.java
- import com.ociweb.dates.Person;
-
- public class Main {
-
- static {
- // Load libPerson.so.
- System.loadLibrary("Person");
- }
-
- public static void main(String[] args) {
- // Note that Person is a C++ class!
- Person p1 = new Person("Mark Volkmann");
- p1.setBirthday("1961/4/16");
- System.out.println(
- p1.getName() + " is " + p1.getAge() + " years old.");
-
- Person p2 = new Person("Amanda Volkmann");
- p2.setBirthday("1985/7/22");
- System.out.println(
- p2.getName() + " is " + p2.getAge() + " years old.");
-
- System.out.println(
- p1.getName() + " is older than " + p2.getName() +
- "? " + p1.isOlderThan(p2));
- }
- }
Running the Java application
# Compile generated Java files.
javac gen/com/ociweb/dates/*.java
# Compile our Java file.
javac -classpath gen Main.java
# Run the Java application.
java -classpath .:gen -Djava.library.path=. Main
The output from this code is
Mark Volkmann is 44 years old.
Amanda Volkmann is 20 years old.
Mark Volkmann is older than Amanda Volkmann? true
Some Indulgence
One of the cool things about SWIG is that it supports many programming languages. Sure, you're reading the Java News Brief, but allow me to show an example for one other language, Ruby.
Running SWIG for Ruby
# Generate C++ wrapper files.
# This processes Person.i.
swig -c++ -ruby *.i
# Compile wrapper code.
rubydir=/usr/local/lib/ruby/1.8/i686-linux
gcc -c -fpic *.cpp *_wrap.cxx \
-I. -I$rubydir -I/usr/local/include/boost_1_33_0
# Link wrapper code.
gcc -shared -fpic Person.o *_wrap.o \
-lboost_date_time-gcc-1_33 -lstdc++ \
-o RubyPerson.so
Ruby application - main.rb
- require 'RubyPerson.so'
- include RubyPerson # RubyPerson is a Ruby module generated by SWIG
-
- p1 = Person.new('Mark Volkmann')
- p1.setBirthday('1961/4/16')
- puts "#{p1.getName()} is #{p1.getAge()} years old."
-
- p2 = Person.new('Amanda Volkmann')
- p2.setBirthday('1985/7/22')
- puts "#{p2.getName()} is #{p2.getAge()} years old."
-
- puts "#{p1.getName()} is older than #{p2.getName()}? " +
- "#{p1.isOlderThan(p2)}"
Running the Ruby application
ruby main.rb
The output is the same as for the Java application.
Conclusion
SWIG greatly simplifies calling C/C++ functions from many programming languages including Java and Ruby. For more information, visit http://www.swig.org. There you'll find a "Tutorial" link and a "Documentation" link that provides a users manual in both HTML and PDF form.