# Writing a high-level wrapper around a Perl library

This document discusses the theory and practice behind writing a wrapper around a typical object-oriented Perl library. We use Pl_LWP_UserAgent as an example. (This is the high-level wrapper around the LWP::UserAgent library).

Don't worry - writing wrappers is really not very hard at all. I hope that you, the reader, will write some wrappers around your favorite Perl libraries and contribute them back to perl4caml development.

## First steps

I'm going to use LWP::UserAgent as my example throughout this document. Substitute that for whatever library you want to wrap up and call from OCaml. First of all make sure you have the library installed and working under Perl, and make sure you have the manual page for that library in front of you:

    perldoc LWP::UserAgent

or follow this link.

## Understanding what we're doing

The low-level Perl module offers two useful functions and a useful datatype which we'll be using extensively. The useful functions are:

Function name | Perl equivalent | Description
--- | --- | ---
`call_class_method` | `$obj = LWP::UserAgent->new (args...)` | Calls a static method or constructor on a class.
`call_method` | `$obj->some_method (args...)` | Calls an instance method on an object.

The useful datatype is called the `Perl.sv` (an abstract type), which represents a scalar value in Perl (anything you would normally write in Perl with a `$`, including numbers, strings, references and blessed objects). To find out more about "SVs" see the perlguts(3) man page.

To see how these three things interact, let's create an `LWP::UserAgent` object and call a method on it:

    # #load "perl4caml.cma";;
    # open Perl;;
    # let sv = call_class_method "LWP::UserAgent" "new" [];;
    val sv : Perl.sv = <abstr>
    # let agent = call_method sv "agent" [];;
    val agent : Perl.sv = <abstr>
    # string_of_sv agent;;
    - : string = "libwww-perl/5.69"

Note how the variable `sv` contains the actual Perl object (an instance of `LWP::UserAgent`). To be quite clear, this is the equivalent of the following Perl code:

    $sv = LWP::UserAgent->new ();
    $agent = $sv->agent ();
    print $agent;

You could actually just continue to use the low-level interface to access Perl objects directly, but there are three problems with this: firstly it's cumbersome because you have to continually convert to and from SVs; secondly it's not type safe (eg. calling `string_of_sv` might fail if the SV isn't actually a string); thirdly there are more pleasant ways to present this interface.

Writing a high-level wrapper around these low-level operations is what is described in the rest of this document ...

## Classes and constructors

Our general plan, therefore, will be to create an OCaml class corresponding to `LWP::UserAgent`, which hides the implementation (the `sv`, the calls to `call_method`, and the conversion of arguments to and from SVs). We will also need to write one or more constructor function.

We will write at least one method for every method exported by the Perl interface. Sometimes we'll write two methods for each Perl method, as in the case where a Perl method is a "getter/setter":

    $ua->agent([$product_id])
       Get/set the product token that is used to identify the user agent
       on the network.  The agent value is sent as the "User-Agent" header
       in the requests.

becomes two methods in the OCaml version:

    class lwp_useragent sv = object (self)
      (* ... *)
      method agent : string
      method set_agent : string -> unit
    end

We will also write at least one function for every constructor or static function exported from Perl.

The OCaml object itself contains the `sv` which corresponds to the Perl SV (ie. the actual Perl object).

Here is the shape of our class:

    (** Wrapper around Perl [LWP::UserAgent] class.
      *
      * Copyright (C) 20xx your_organisation
      *
      * $ Id $
      *)

    open Perl

    let _ = eval "use LWP::UserAgent"

    class lwp_useragent sv = object (self)

      The methods will go here ...

    end

    (* The "new" constructor. Note that "new" is a reserved word in OCaml. *)
    let new_ ... =
      ...
      let sv = call_class_method "LWP::UserAgent" "new" [args ...] in
      new lwp_useragent sv

    Any other static functions will go here ...

Notice a few things here:

1. There is some ocamldoc describing the class.
2. We "open Perl" to avoid having to prefix everything with `Perl.`.
3. We `eval "use LWP::UserAgent"` when the module is loaded. This is required by Perl.
4. The `sv` (scalar value representing the actual object) is passed as a parameter to the class.

## Writing methods

### Getters and setters

Of all types of methods, getters and setters are the easiest to write. First of all, check the manual page to find out what type the slot is. You'll need to write one get method and one set method. (Rarely you'll find getters and setters which are quasi-polymorphic, for instance they can take a string or an arrayref. You'll need to think more deeply about these, because they require one set method for each type, and the get method can be complicated).

Here's our getter and setter for the agent slot, described above. The agent is a string:

      method agent =
        string_of_sv (call_method sv "agent" [])
      method set_agent v =
        call_method_void sv "agent" [sv_of_string v]

Note:

1. The get method is just called `agent` (not "get_agent"). This is the standard for OCaml code.
2. We use `string_of_sv` and `sv_of_string` to convert to and from SVs. This will ensure that the class interface will have the correct type (string), and thus be type safe as far as the calling code is concerned, and also means the caller doesn't need to worry about SVs.
3. The set method called `call_method_void` which we haven't seen before. This is exactly the same as `call_method` except that the method is called in a "void context" - in other words, any return value is thrown away. This is slightly more efficient than ignoring the return value.

Here's another example, with a boolean slot:

      method parse_head =
        bool_of_sv (call_method sv "parse_head" [])
      method set_parse_head v =
        call_method_void sv "parse_head" [sv_of_bool v]

### Ordinary methods

Other methods are perhaps simpler to wrap than getters and setters. `LWP::UserAgent` contains an interesting method called `request` (which actually runs the request).

What's particularly interesting about this method are the parameter and return value. It takes an `HTTP::Request` object and returns an `HTTP::Response`.

I have already wrapped `HTTP::Request` and `HTTP::Response` as modules Pl_HTTP_Request and Pl_HTTP_Response respectively. You should go and look at the code in those modules now.

If `request` requires a parameter, what should that parameter be? Naturally it should be the SV corresponding to the `HTTP::Request` object. To get this, I provided an `#sv` method on the `http_request` class.

And what will `request` return? Naturally it will return an SV which corresponds to the (newly created inside Perl) `HTTP::Response` object. We need to wrap this up and create a new OCaml `http_response` object, containing that SV.

This is what the final method looks like:

      method request (request : http_request) =
        let sv = call_method sv "request" [request#sv] in
        new http_response sv

It's actually not so complicated.

## Writing constructors and static functions

Constructors are fairly simple, although the `new_` function inside `Pl_LWP_UserAgent` is complicated by the many optional arguments which `LWP::UserAgent->new` can take.

Here is the guts, omitting all but one of the optional args:

    let new_ ?agent (* ... *) () =
      let args = ref [] in
      let may f = function None -> () | Some v -> f v in
      may (fun v ->
    	 args := sv_of_string "agent" :: sv_of_string v :: !args) agent;
    (* ... *)
      let sv = call_class_method "LWP::UserAgent" "new" !args in
      new lwp_useragent sv

It works simply enough, first building up a list of `sv`s corresponding to the arguments, then calling `call_class_method` to create the Perl object, then returning a constructed OCaml `lwp_useragent` object containing that `sv`.

## Contributing wrappers back to perl4caml

If you write a wrapper for a Perl class, particularly one from CPAN, I urge you to contribute it back to the perl4caml development effort. Your contribution enriches the project as a whole, and makes OCaml more useful too.

Last modified: Thu Oct 16 14:39:02 BST 2003