The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

pickle - C++ access to the Perl interpreter

SYNOPSIS

    #include <pickle.hh>
    using namespace Pickle;

    Interpreter::vivify ();

    eval_string ("use Cwd;");
    Scalar cwd = call_function ("cwd");
    cout << "cwd is " << string (cwd) << endl;

    eval_string ("use File::Glob ':glob';");
    List args = List () << "~/*.doc"
                        << call_function ("GLOB_TILDE");
    Arrayref files = call_function ("glob", args, LIST);
    for (size_t i = 0; i < files.size(); i++)
        cout << string(files[i]) << endl;

    Scalar doit (Scalar& n)
    {
        return double (n) * double (n);
    }
    define_sub ("main", "square", doit);
    cout << int (eval_string ("square(42)")) << endl;

    try { call_some_perl() }
    catch (Exception* e) { cerr << e->what(); delete e; }
    throw new Exception ("Your fault");

DESCRIPTION

Pickle is a C++ library that allows programs to embed and interact with the Perl interpreter more conveniently than by directly using Perl's native C support.

Pickle allows programs and Perl modules written in C++ to evaluate Perl code, create and examine Perl data structures, and make functions callable from Perl. It is well suited for large applications prototyped in Perl and transitioned piece by piece to C++, since it allows one to move the boundary between implementation languages quickly. It achieves this by exposing a minimally sufficient API to express the Perl language's semantics, independently of Perl's implementation. The simplicity of this approach greatly reduces the likelihood of bugs in ``glue'' code between the two languages.

The rest of this document assumes knowledge of Perl and C++.

Unless otherwise specified, the functions and data types here are declared in <pickle.hh> in namespace Pickle and defined in -lpickle. To use them in a C++ program, you must link with the Perl interpreter, -lperlint.

Scalars

Pickle uses class Scalar to hold Perl scalar values:

    class Scalar;

The default Scalar constructor creates an undef value. You can initialize scalars with any C++ integral or floating point type, as well as types bool, string and C string (char *).

    Scalar s0;               // undef
    Scalar s1 (0);           // integer
    Scalar s2 (3.14);        // float
    Scalar s3 ("hello");     // string

Class Scalar defines conversion operators for the same C++ types, so you can use Scalar objects where these types are expected:

    Scalar s;
    int i (s);
    string str (s);
    x = r * cos(s);

As in Perl, scalars are automatically reference-counted, so memory management is not much of an issue, unless you create cyclic structures as described in "Two-Phased Garbage Collection" in perlobj.

Arrayrefs and Hashrefs

Scalar variables can hold array and hash references. Pickle defines subclasses of Scalar named Arrayref and Hashref for these two reference types. Their default constructors produce empty containers, the same as the Perl expressions [] and {}:

    Arrayref a;              // 0-element anonymous array
    Hashref h;               // empty hash table

Arrayref and Hashref can also be constructed from scalars. By default, these constructors perform type verification, throwing an exception if the Perl reference data type does not match the C++ class. This check can be overridden by passing false as a second argument to the constructor.

    Arrayref a;
    Scalar some_func();
    a = some_func();         // okay if an arrayref was returned.
    a = Scalar (5);          // runtime error - not an array reference.
    Scalar x ("hello");
    a = Arrayref (x, false); // may lead to a crash!

The Arrayref and Hashref classes both define methods fetch and store for retrieving and inserting elements. Additionally, Arrayref overloads the [] operator, so you can use C++ array syntax to fetch arrayref elements. However, unlike fetch, [] extends the array if the given index is past the end.

    Arrayref a;
    a .push (17);            // a[0] == 17
    a[1] = a[5];             // a now has 6 elements!
    a .store (10, a .fetch (20));
                             // a.size() == 11

Arrayref's push method pushes a scalar onto the end of the array. size returns the array length.

    Hashref h;
    h .store ("array", a);
    a = h .fetch ("other");

Iteration over a hash currently is not supported.

Lists and Functions

In Perl, every function takes a list of scalar arguments and returns a list of scalar results. In scalar context, the return list is forced to contain exactly one item, while in void context the result list is discarded.

Pickle's call_function lets you call functions in any context - list, scalar, or void - by specifying either SCALAR, LIST, or VOID as its final argument. Because C++ does not have Perl's notion of context, call_function always returns a Scalar. By convention, list context returns an Arrayref object whose array elements are the result list. Void context returns undef.

Perl subs can be called with any number of arguments. (Not if they are prototyped, but Pickle does not honor Perl prototypes.) Passing variable-length argument lists in C++ is awkward, so Pickle defines a class, List, for holding argument lists.

A List object is very much like an arrayref. In fact, it contains an Arrayref and conversion operators for types Arrayref & and const Arrayref &. Lists and arrayrefs are defined as different types to make certain overloaded functions work and to emphasize their different uses. Perl also distinguishes between lists and arrays, though they are both just sequences of scalars.

The default list constructor creates an empty list. The << (left bitshift) operator is overloaded for the List type similarly to the standard ostream class; it takes a scalar on the right, appends it to the list, and returns the list. It allows one to build lists using chains like this:

    List l;
    l << Scalar(3) << Scalar("caballeros");

Taking advantage of C++'s conversion and overloading rules, you could rewrite this as:

    List l = List () << 3 << "caballeros";

call_function requires as its first argument a Scalar, which can be the name of a function or a code reference. Then comes an optional List argument containing the function args. If no list is given, the sub is called without arguments. The calling context may be specified as the final argument. If present, it must be either SCALAR, LIST, or VOID. The default context is scalar.

As an example, this code calls the function Carp::confess with argument "I'm sorry":

    call_function ("Carp::confess", List () << "I'm sorry");

call_function works only with user-defined subs, not Perl's builtin operators such as print.

Methods

If a Scalar variable holds a package name or blessed reference, you can call methods on it using the C++ call_method method. Like call_function, it takes an optional List of the arguments and an optional context specifier. For example, this code calls the Perl method fetchrow_array on the object sth with no arguments in list context:

    Arrayref result = sth .call_method ("fetchrow_array", LIST);

Evaluating Perl Code

In addition to calling existing functions, Pickle allows you to evaluate strings of Perl code using the eval_string function:

    Scalar result = eval_string ("$x + 4 * $z");

eval_string supports only scalar context.

Defining Functions

Pickle supports creating Perl subs that call C++ functions (called extension subs or XSubs). Your C++ function may have any of three supported prototypes. The first form requires exactly one scalar argument and returns a scalar.

    Scalar one_arg_xsub (Scalar& arg);

The following form takes an object followed by a list of name/value pairs, which Pickle loads into a hash. It is designed for methods that support the $obj->meth (name => value, ...) calling convention.

    Scalar arg_hash_xsub (Scalar& arg, Hashref& args);

The third form is the most general. It takes a list of arguments and a context specifier and returns a list of results.

    List arg_list_xsub (List& args, Context context);

Here context will be either LIST, SCALAR, or VOID.

To make a function callable from Perl, use the define_sub function. define_sub takes as arguments a package name, a sub name, and a pointer to the XSub. This example creates a sub named doit in package main, calls it with 17 as the argument, and displays the result as an integer:

    static Scalar
    my_func (Scalar& x)
    {
        return int (x) * int (x);
    }

    // ...elsewhere...
    define_sub ("main", "doit", my_func);
    cout << int (eval_string ("main::doit (17)")) << endl;

See "C++ in a Perl Program" for a more detailed example.

Using Exceptions

When Perl code under the control of C++ ``dies,'' Pickle throws an exception of type Exception *. The error message can be obtained by calling its what method. The exception must be freed with delete.

Example:

    try
    {
        eval_string ("$x = 0; 4 / $x");
    }
    catch (Exception* e)
    {
        cerr << "Perl error: " << e->what () << endl;
        delete e;
    }

XSubs created with Pickle can construct an Exception object with a Scalar argument and throw a pointer to it. Pickle deletes the object. Perl can trap such exceptions using eval.

Example:

    Scalar my_xsub (Scalar& s)
    {
        if (double (s) < 0)
            throw new Exception (string ("'") .append (s)
                                 .append ("' is a negative number"));
        return sqrt (double (s));
    }

Perl in a C++ Program

There are two ways to link Perl and C++ code together. Either a Perl program loads a module implemented in C++, or a C++ program embeds the Perl interpreter. Here, ``interpreter'' means something akin to ``virtual machine'' in Java parlance. The interpreter is the C++ object that implements the environment in which Perl programs are run.

To use Pickle in a C++ program, you must link with -lpickle -lperlint. You may use the functions described in perlembed to initialize the interpreter, or you may use Pickle's simpler interface. Just allocate an object of class Interpreter, like this:

    Interpreter* interp = new Interpreter;

There is a constructor that accepts command line arguments and an optional environment pointer like the C++ function main. This can be used to pass flags to Perl. For example, this turns on taint checks and warnings:

    char* argv[] = { "perl", "-Tw", "-e0" };
    interp = new Interpreter (sizeof argv / sizeof argv[0], argv);

Note that -e0 prevents the interpreter from trying to read a script from the standard input.

The default Perl configuration will not tolerate more than one interpreter at a time in the same process. Most Pickle functions act implicitly on the ``current'' interpreter, so the Interpreter object is generally not used. You can cause Perl to shut down and free its memory by deallocating it, as in:

    delete interp;

This will cause problems with any Perl data held in subsequently destroyed C++ objects, because the destructor will try to interact with the nonexistent interpreter.

You can check to see if an interpreter has been initialized in the current process using Interpreter::ping(). If this function returns true, Perl objects may be created and manipulated. This is useful in code intended to run under either Perl or C++.

As a convenience, Interpreter::vivify() is defined to construct and return a new interpreter unless one already exists, like this:

    Interpreter* Interpreter::vivify ()
    {
      return Interpreter::ping () ? 0 : new Interpreter;
    }

C++ in a Perl Program

perlxs and ExtUtils::MakeMaker describe Perl's officially supported way to link C and C++ code into a Perl module. Pickle is implemented using the interface described in perlapi, but does not currently support interoperation with that interface. (It would be nice if it did, but if you know Perl's API, you are qualified to hack on Pickle and get around this limitation.) However, with the combination of define_sub and XS's BOOT keyword, the Perl API often isn't needed.

Here is an example XS file that defines a sub named Foo::squarit. Indentation is used for readability, but must be removed.

    // Foo.xs
    #include <pickle.hh>
    using namespace Pickle;

    static Scalar
    squarit (Scalar& it)
    {
        return double(it) * double(it);
    }

    static void
    init ()
    {
        define_sub ("Foo", "squarit", squarit);
    }

    // Always include these three here.
    #include <EXTERN.h>
    #include <perl.h>
    #include <XSUB.h>

    // Change Foo to your module's name
    MODULE = Foo        PACKAGE = Foo

    BOOT:
            init();

Here is a Makefile.PL to build the module:

    # Makefile.PL
    # Usage: perl Makefile.PL && make
    use ExtUtils::MakeMaker;

    WriteMakefile
        (
         'NAME'       => 'Foo',
         'VERSION'    => '0.0',
         'XSOPT'      => '-C++',
         'CC'         => 'g++',
         'LD'         => 'g++',
         'LIBS'       => '-lpickle',
         'dynamic_lib' => { 'OTHERLDFLAGS' =>
                            '-Wl,-R,$(PREFIX)/lib -L$(PREFIX)/lib',
                          },
        );

And you'll need Foo.pm to load it:

    # Foo.pm
    # Usage: perl -Mblib -MFoo -le "print Foo::squarit(253)"
    package Foo;

    use XSLoader;
    XSLoader::load ('Foo');

    1;

Refer to perlmod, ExtUtils::MakeMaker, and perlmodinstall for general information about writing and installing modules.

LICENSE

Copyright (C) 2000 by John Tobey, jtobey@john-edwin-tobey.org. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307  USA

If included in the official Perl distribution by Larry Wall or his agent (``Pumpking''), it may be distributed under the same terms as the official Perl distribution. For other licensing arrangements, contact the author.

SEE ALSO

XSLoader, perlmod, ExtUtils::MakeMaker, perlembed, perlxs