Gluing C++ And Perl Together

虽然是2001年的文章,但是还是学习一下:

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:

  1. Run h2xs -A -n MyPackage
  2. 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

  3. Put the file perlobject.map (found in Dean Roehrich's directory) in the MyPackage directory.
  4. Create a MyPackage/typemap file with the following text:

    TYPEMAP
    MyClass * O_OBJECT
  5. Add 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):

  1. 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.
  2. 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.
  3. 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?
  4. 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.
  5. 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.
  6. 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.
  7. SWIG is supposed to be a wonderful (and simpler) alternative to Perl/XS. I couldn't get it to work, though. :(
  8. Inline is another package that does this stuff. I have not looked into it. Time is the enemy of us all ...
Copyright (C) 2001 John Keiser (john@johnkeiser.com). Redistribute freely, just keep this copyright with file.

Monthly Archives

Pages

Powered by Movable Type 7.7.2

About this Entry

This page contains a single entry by Cnangel published on May 21, 2009 4:25 PM.

Some useful perl + XS + C URLs was the previous entry in this blog.

Yum使用 is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.