160 likes | 302 Views
Wrapping C++ with Perl. Brian Magill March 2009. SWIG (Simplified Wrapper and Interface Generator). Wraps C++ with Perl or other computer languages Generates the connecting wrapper code for both C++ and Perl (or other target language). C++ code then compiled and linked into a library
E N D
Wrapping C++ with Perl Brian Magill March 2009
SWIG (Simplified Wrapper and Interface Generator) • Wraps C++ with Perl or other computer languages • Generates the connecting wrapper code for both C++ and Perl (or other target language). • C++ code then compiled and linked into a library • Perl scripts call Perl wrapper, which in turn calls C++ library
Motivation • Code high level functionality in Perl: • File, Database I/O • String manipulation, pattern matching, etc. • Sorting, organizing data • Applying functions to elements of arrays • Filtering out elements from arrays • Code Toolkit interfaces and computationally intensive code in C++ • Need a better way to integrate these two languages
File Generation example.i example.pm SWIG example.h example_wrap.cxx (other header files) example_wrap.cxx example.dylib (MacIntosh) g++ example.cpp or example.so (Other Unix Platforms) (other CPP files)
SWIG Interface File • Tells which routines are to be wrapped • Wraps built-in C/C++ data types • Wraps STL vectors and strings • Can wrap more complex data structures • Can specify error handling
Example Interface File %include "exception.i" %exception { try { $action } catch (const std::exception &e) { SWIG_exception_fail(SWIG_RuntimeError, e.what()); } } %module example %{ /* Put headers and other declarations here */ #include "example.h" %} %include "std_vector.i" %include "std_string.i" %template(IntVector) std::vector<int>; %template(DoubleVector) std::vector<double>; %include "example.h"
Running the SWIG Command • swig –c++ -perl5 example.i • Produces: • example_wrap.cxx … C++ Wrapper • example.pm … Perl Wrapper
Compiling and Linking C++ Example # # Make file: MacIntosh version # all: swig -c++ -perl5 example.i g++ -c example.cpp example_wrap.cxx \ `/usr/bin/perl -MExtUtils::Embed -e ccopts` g++ -dynamiclib -single_module -flat_namespace \ -undefined suppress -o example.dylib example.o example_wrap.o clean: rm *.o *.cxx purge: rm *.o *.dylib *.cxx *.pm
Compiling and Linking C++ Example # # SWIG example make file for Linux # all: swig -c++ -perl example.i g++ -c example.cpp example_wrap.cxx \ `/usr/bin/perl -MExtUtils::Embed -e ccopts` g++ -shared example.o example_wrap.o -L /usr/lib/ \ -lperl -o example.so clean: rm *.o *.cxx purge: rm *.o *.so *.cxx *.pm
Resulting C++ Library • Differs with platform and operating system • On Mac OS X a *.dylib library is created • On Unix Systems a shared (*.so) library is created • C++ library needs to be in same location as Perl wrapper.
Simple Example /* Example.h */ #include <vector> #include <string> #define PGSd_EOS_AM 2222 #define PGSd_EOS_PM 3333 double average(std::vector<int> v); std::vector<double> half(const std::vector<double> & v); std::string TestMod(const std::string &s);
Simple Example (Con’t) // // example.cpp // #include <algorithm> #include <functional> #include <numeric> #include "example.h" using namespace std; double average(vector<int> v) { return (accumulate(v.begin(), v.end(), 0.0))/v.size();} vector<double> half(const vector<double> & v) { vector<double> w(v); for( unsigned long i = 0; i < w.size(); i++) w[i] /= 2.0; return w; } std::string TestMod(const std::string &s) {return string("TestOutput: ") + s;}
#!/usr/bin/perl # # TestExample.pl # use strict; use example; my @intV = (0..3); my @dblV = (0.0, 1.5, 1.0, 1.5); print "Integer Array Values: "; map { print "$_ ,"; } @intV; print "\nAverage = ", example::average(\@intV), "\n"; print "\nTry passing in an array of doubles: \n"; eval {"Average = ", example::average(\@dblV), "\n"; }; print "\tError encountered: $@\n" if ($@); # print "Double Array Values: "; map { print "$_ ,"; } @dblV; my $ref = example::half(\@dblV); print "\nHalved Values: "; map { print "$_ ,"; } @$ref; print "\n\n"; # my $str = "Hello World!\n"; print example::TestMod($str); print "\nConstant PGSd_EOS_AM = ", $example::PGSd_EOS_AM, "\n"; print "Constant PGSd_EOS_PM = ", $example::PGSd_EOS_PM, "\n\n";
TestExample.pl’s Output Integer Array Values: 0 ,1 ,2 ,3 , Average = 1.5 Try passing in an array of doubles: Error encountered: RuntimeError Type error in argument 1 of average. Expected an array of int Double Array Values: 0 ,1.5 ,1 ,1.5 , Halved Values: 0 ,0.75 ,0.5 ,0.75 , TestOutput: Hello World! Constant PGSd_EOS_AM = 2222 Constant PGSd_EOS_PM = 3333
Suggestions • Use built-in or STL types arguments wherever possible • Otherwise use custom built C++ data classes consisting of above types • Link C++ code as shared objects if possible • Place exception handling first in interface file • Use SWIG_exception_fail
References • http://www.swig.org • http://home.pacbell.net/ouster/scripting.html • /CERES/instrument/home/bmagill/ComputerLanguages/Perl/SWIG/SWIG_C++/ • /CERES/instrument/home/bmagill/SolarAngle/Configured/src/