August 27, 2001
Perl XS (the Perl native glue) and C++ hookup is not clearly covered in any one reference that I can find. So here's some coverage.
1. Preparing the Installation
XS totally supports C++. XS is not the problem. Getting MakeMaker happy is the problem. And even that is just a few words. Here's the steps to create a working C++ project from scratch:
- Run
h2xs -A -n MyPackage
Edit
MyPackage/MyPackage.xs
(the XS/C++ source) to add extern "C" around the headers:#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif- Put the file
perlobject.map
(found in Dean Roehrich's directory) in theMyPackage
directory. Create a
MyPackage/typemap
file with the following text:TYPEMAP
MyClass * O_OBJECTAdd C++ directives to
MyPackage/Makefile.PL
, like so:<Makefile.PL>
use ExtUtils::MakeMaker;
$CC = 'g++';
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
'NAME' => 'MyPackage',
'VERSION_FROM' => 'MyPackage.pm', # finds $VERSION
'PREREQ_PM' => {}, # e.g., Module::Name => 1.1
($] >= 5.005 ? ## Add these new keywords supported since 5.005
(ABSTRACT_FROM => 'MyPackage.pm', # retrieve abstract from module
AUTHOR => 'John Keiser <john@johnkeiser.com>') : ()),
'LIBS' => [], # e.g., '-lm'
'DEFINE' => '', # e.g., '-DHAVE_SOMETHING'
'CC' => $CC,
'LD' => '$(CC)',
# Insert -I. if you add *.h files later:
'INC' => "", # e.g., '-I/usr/include/other'
# Un-comment this if you add C files to link with later:
# 'OBJECT' => '$(O_FILES)', # link all the C files too
'XSOPT' => '-C++',
'TYPEMAPS' => ['perlobject.map' ],
);
That's all you have to do, basically. You might have a problem if you use the C++ standard library "list" class, so see below if you have that.
What this procedure does: Step 1 creates the actual package. Step 2 makes the Perl headers compile and link correctly. Step 3 adds the type mapping necessary to get object pointers to map to Perl objects. Step 4 causes MakeMaker (and consequently make) to use C++ to compile and link the program.
1.1 Running the Program
To run the program, just do:
perl Makefile.PL
make test
The examples in the next section are best placed into test.pl
, which will automatically run when
you do make test
. It sets the variables and paths and such correctly. You will have to do
make install
before you can properly do use MyPackage;
in Perl.
2. Programming the C++/Perl Interface
Once you have the files set up (the hard part) you get to do the programming, which is pretty easy. I am not going to go over XS programming here; it's covered acceptably (though not as thoroughly as one would like) in the references below.
The typemap you copied into the directory handles mapping between Perl scalars and MyType *. In other words, the Perl object now represents a pointer to your object, without you having to do anything special to make it happen except declare new()! (And you don't have to do any work in new either, XS handles that.
2.1 Straight C++
First, you should know that anything you put before that MODULE=... line will be compiled as straight C++. XS will not do any special processing to it, it will go straight up to the C++ compiler the way it is.
You might put a custom class definition here (or you can use a C++ class definition already in a header file). You might put some custom functions here. You might put some global variables here. You might ignore me and skip to the next section on constructors.
2.2 Define Your Class
XS won't define your class for you. It is expecting a class to already be there. Either include the header file with the class in it or make a class definition in the straight C++ section.
2.3 Constructors/Destructors
This seems like a scary subject until you actually just put MyClass::new() and MyClass::DESTROY() functions into the file. XS does the mapping automatically. It even supports constructor args. Yay!
Example
<MyPackage.xs>
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
#include <iostream.h>
class MyClass {
public:
MyClass(char * my_favorite_argument) {
cout << "I'm constructin' my bad self ... " << my_favorite_argument << "\n";
}
~MyClass() { cout << "Destruction is a way of life for me.\n"; }
};
MODULE = MyPackage PACKAGE = MyPackage
MyClass *
MyClass::new(char * my_favorite_argument)
void
MyClass::DESTROY()
Note here how MyClass and MyPackage don't have to have the same name. You can name the Perl package anything you want. XS will map the C++ functions to Perl functions based on the names you give the functions and the most recent PACKAGE directive.
Test
Put this into test.pl at the bottom:
my $x = new MyPackage("Hi.");
This test will output:
I'm constructin' my bad self ... Hi.
Destruction is a way of life for me.
2.4 Member Functions
If you have simple non-list, non-hash return values and arguments, you can write the code for your
member function as a member of the class, and just add
<ret> MyClass::myFunction(<args>)
after the MODULE= line. XS will automagically
hook the Perl function up to the class member function and return its return value.
Example
<MyPackage.xs>
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
#include <iostream.h>
class MyClass {
public:
MyClass(char * my_favorite_argument) {
cout << "I'm constructin' my bad self ... " << my_favorite_argument << "\n";
}
~MyClass() { cout << "Destruction is a way of life for me.\n"; }
int wow() { return 12 / 3; }
};
MODULE = MyPackage PACKAGE = MyPackage
MyClass *
MyClass::new(char * my_favorite_argument)
void
MyClass::DESTROY()
int
MyClass::wow()
Test
Put this into test.pl at the bottom (remove the previous test):
my $x = new MyPackage("Hi.");
print $x->wow, " is the magic number.\n";
This test will output:
I'm constructin' my bad self ... Hi.
4 is the magic number.
Destruction is a way of life for me.
2.5 XS-Laden Member Functions
If you have to return a list or hash or accept a list or hash argument, or do other funky Perl stuff,
you should use PPCODE. See the references below to learn how to code these. The only special thing that
happens when you create a C++ member function is you get a MyClass *
named THIS
.
So you can call functions like this: THIS->memberFunc()
.
Note that when you create a member function with PPCODE you do not have to have a corresponding class member function (though you can if you want, it just won't be called automatically). The PPCODE is the function.
Example
<MyPackage.xs>
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
#include <iostream.h>
class MyClass {
public:
MyClass(char * my_favorite_argument) {
cout << "I'm constructin' my bad self ... " << my_favorite_argument << "\n";
}
~MyClass() { cout << "Destruction is a way of life for me.\n"; }
int wow() { return 12 / 3; }
};
MODULE = MyPackage PACKAGE = MyPackage
MyClass *
MyClass::new(char * my_favorite_argument)
void
MyClass::DESTROY()
int
MyClass::wow()
void
MyClass::wow2()
PPCODE:
for(int i=1;i<=10;i++) {
XPUSHs(sv_2mortal(newSVnv(i * THIS->wow())));
}
Test
Put this into test.pl at the bottom (remove the previous test):
my $x = new MyPackage("Hi.");
print "Multiples of 4: ", join(", ", $x->wow2()), "\n";
This test will output:
I'm constructin' my bad self ... Hi.
Multiples of 4: 4, 8, 12, 16, 20, 24, 28, 32, 36, 40
Destruction is a way of life for me.
2.6 Member Variables
You'd think you could access member variables from Perl like a hash, right? Not recommended. Only functions are supported, though if you wanted you could create a hash and tie it to the class. Use SWIG and shadow classing if you want to access fields in a hash.
3. C++ stdlib and list.h
Perl has a conflict with the C++ standard library: they both define the "list" structure. There are
two ways of solving this, depending on whether you are actually interested in using the list structure or
not. If you don't need the list (like if just the headers need it) or you need Perl's list structure for
some reason, you should just include your header files (and/or stdlib) before the standard Perl include
files. For example, if you have a header file <myfile.h>
that includes
<list>
, do this:
#include <myfile.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
If you want to use the C++ stdlib list object in the Perl file itself, you need to #undef list and
then include your files after the standard Perl headers (including <list>
if your
header files do not include list).
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
#undef list
#include <myfile.h>
#include <list>
If one method doesn't work, try the other. If you need both in one file, you may be able to use the first
method, put your functions that require the C++ list last in the file, and then do #undef list
and #include <list>
just before you . This is untested though, and really not recommended
because it's ugly. Thanks to
Tye McQueen for the info that
led to this section.
4. General XS Pointers
XS is pretty cool, and actually fairly simple once you understand it. The tutorial and manpage for XS give you some decent basic information, but they won't tell you everything you need to know to get an XS package off the ground. Here are all the pointers I found (in order of their usefulness to me in figuring stuff out):
- The Perl XS Tutorial man page ("man perlxstut") is the first place you should go when you know nothing at all about XS. Not that it will give you enough information to do what you want to do, mind you, but it will get you started in the basics rather admirably.
- Perl Guts and Perl API man pages ("man perlguts" and "man perlapi") are very useful in figuring out how to actually talk to Perl directly when you need something a little more than the C/C++ hookup. Like when you want to return arrays, for example. Guts is a more structured explanation, API is a more complete reference.
- The XS Mailing List is the place where at least a few fairly advanced developers talk about XS. Answers to newbie questions are sporadic but very useful. Strange URL: develooper.com?
- Dean Roehrich's Examples are great for getting moving. CookBookA and CookBookB in this directory are what you want. CookBookB in particular has a C++ example and many structure examples.
- The Perl XS man page ("man perlxs") is where you'd expect you'd want to go, right? Well, I am not sure what the heck is wrong with it (or me) but I couldn't extract much of any useful information from it. Maybe you'll have better luck, though.
- MakeMaker is the tool you use to actually compile and install your stuff. Unfortunately this documentation by itself is inadequate for figuring out how to actually perform what you want to do--this is more of a reference than an introduction. I have not found a sufficient MakeMaker introduction to date, even though it is the foundation of CPAN and almost all Perl packaging and a damn cool tool.
- SWIG is supposed to be a wonderful (and simpler) alternative to Perl/XS. I couldn't get it to work, though. :(
- Inline is another package that does this stuff. I have not looked into it. Time is the enemy of us all ...