January 2006: Simplified Wrapper and Interface Generator (SWIG)

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

Why would you want to do such a thing? The most common reasons are

  1. Performance 
    The C/C++ implementation may be faster than what could be implemented in the calling language.
  2. 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.

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

  1. #ifndef PERSON_H
  2. #define PERSON_H
  3.  
  4. // This example uses the Boost date_time library to
  5. // store dates and perform calculations using them.
  6. #include "boost/date_time/gregorian/gregorian.hpp"
  7. #include <string>
  8.  
  9. class Person {
  10.  
  11. public:
  12.  
  13. Person(const std::string& name);
  14.  
  15. // Call this before calling getAge,
  16. // getBirthday or isOlderThan.
  17. void setBirthday(const std::string& birthday);
  18.  
  19. int getAge() const;
  20. boost::gregorian::date getBirthday() const;
  21. std::string getName() const;
  22. bool isOlderThan(const Person& person) const;
  23.  
  24. private:
  25.  
  26. boost::gregorian::date birthday_;
  27. std::string name_;
  28.  
  29. };
  30.  
  31. #endif

C++ source file - Person.cpp

  1. #include "Person.h"
  2.  
  3. using namespace boost::gregorian;
  4. using namespace std;
  5.  
  6. Person::Person(const string& name) : name_(name) {
  7. }
  8.  
  9. int Person::getAge() const {
  10. date today(day_clock::local_day());
  11. int days1 = today.day_of_year();
  12. int days2 = birthday_.day_of_year();
  13.  
  14. int years = today.year() - birthday_.year();
  15. if (days1 < days2) --years;
  16. return years;
  17. }
  18.  
  19. date Person::getBirthday() const { return birthday_; }
  20.  
  21. string Person::getName() const { return name_; }
  22.  
  23. bool Person::isOlderThan(const Person& person) const {
  24. return birthday_ < person.birthday_;
  25. }
  26.  
  27. void Person::setBirthday(const string& birthday) {
  28. birthday_ = from_string(birthday);
  29. }

SWIG input - Person.i

%module RubyPerson
 
%{
#include "Person.h"
%}
 
%include "std_string.i"
%include "Person.h"

Notes about the above file:

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

  1. import com.ociweb.dates.Person;
  2.  
  3. public class Main {
  4.  
  5. static {
  6. // Load libPerson.so.
  7. System.loadLibrary("Person");
  8. }
  9.  
  10. public static void main(String[] args) {
  11. // Note that Person is a C++ class!
  12. Person p1 = new Person("Mark Volkmann");
  13. p1.setBirthday("1961/4/16");
  14. System.out.println(
  15. p1.getName() + " is " + p1.getAge() + " years old.");
  16.  
  17. Person p2 = new Person("Amanda Volkmann");
  18. p2.setBirthday("1985/7/22");
  19. System.out.println(
  20. p2.getName() + " is " + p2.getAge() + " years old.");
  21.  
  22. System.out.println(
  23. p1.getName() + " is older than " + p2.getName() +
  24. "? " + p1.isOlderThan(p2));
  25. }
  26. }

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

  1. require 'RubyPerson.so'
  2. include RubyPerson # RubyPerson is a Ruby module generated by SWIG
  3.  
  4. p1 = Person.new('Mark Volkmann')
  5. p1.setBirthday('1961/4/16')
  6. puts "#{p1.getName()} is #{p1.getAge()} years old."
  7.  
  8. p2 = Person.new('Amanda Volkmann')
  9. p2.setBirthday('1985/7/22')
  10. puts "#{p2.getName()} is #{p2.getAge()} years old."
  11.  
  12. puts "#{p1.getName()} is older than #{p2.getName()}? " +
  13. "#{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.

 

The Software Engineering Tech Trends is a monthly publication featuring emerging trends in software engineering.

Subscribe

© Copyright Object Computing, Inc. 1993, 2018. All rights reserved

secret