1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
4 <title>Writing a high-level wrapper around a Perl library</title>
5 <link rel="stylesheet" href="http://www.merjis.com/css/default.css"
9 <body bgcolor="#ffffff">
10 <h1>Writing a high-level wrapper around a Perl library</h1>
13 This document discusses the theory and practice behind writing
14 a wrapper around a typical object-oriented Perl library. We
15 use <a href="../html/Pl_LWP_UserAgent.html">Pl_LWP_UserAgent</a>
16 as an example. (This is the high-level wrapper around the
17 <a href="http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm">LWP::UserAgent</a>
22 Don't worry - writing wrappers is really not very hard at all. I
23 hope that you, the reader, will write some wrappers around your
24 favorite Perl libraries and contribute them back to
25 <a href="http://www.merjis.com/developers/perl4caml/">perl4caml</a>
32 I'm going to use <a href="http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm">LWP::UserAgent</a>
33 as my example throughout this document. Substitute that for whatever
34 library you want to wrap up and call from OCaml.
35 First of all make sure you have the library installed and working
36 under Perl, and make sure you have the manual page for that
37 library in front of you:
41 perldoc LWP::UserAgent
45 or <a href="http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm">follow
49 <h2>Understanding what we're doing</h2>
52 The low-level <a href="../html/Perl.html">Perl</a> module offers
53 two useful functions and a useful datatype which we'll be using
54 extensively. The useful functions are:
59 <th> Function name </th>
60 <th> Perl equivalent </th>
61 <th> Description </th>
65 <td> <code>call_class_method</code> </td>
66 <td> <code>$obj = LWP::UserAgent->new (args...)</code> </td>
68 <p> Calls a static method or constructor on a class. </p>
73 <td> <code>call_method</code> </td>
74 <td> <code>$obj->some_method (args...)</code> </td>
76 <p> Calls an instance method on an object. </p>
82 The useful datatype is called the <code>Perl.sv</code> (an
83 abstract type), which represents a scalar value in Perl (anything
84 you would normally write in Perl with a <code>$</code>, including
85 numbers, strings, references and blessed objects). To find out
86 more about "SVs" see <a href="http://www.perldoc.com/perl5.8.0/pod/perlguts.html">the perlguts(3) man page</a>.
90 To see how these three things interact, let's create an
91 <code>LWP::UserAgent</code> object and call a method on it:
95 # #load "perl4caml.cma";;
97 # let sv = call_class_method "LWP::UserAgent" "new" [];;
98 <i>val sv : Perl.sv = <abstr></i>
99 # let agent = call_method sv "agent" [];;
100 <i>val agent : Perl.sv = <abstr></i>
101 # string_of_sv agent;;
102 <i>- : string = "libwww-perl/5.69"</i>
106 Note how the variable <code>sv</code> contains the actual Perl
107 object (an instance of <code>LWP::UserAgent</code>). To be
108 quite clear, this is the equivalent of the following Perl code:
112 $sv = LWP::UserAgent->new ();
113 $agent = $sv->agent ();
118 You could actually just continue to use the low-level interface
119 to access Perl objects directly, but there are three problems with
120 this: firstly it's cumbersome because you have to continually
121 convert to and from SVs; secondly it's not type safe
122 (eg. calling <code>string_of_sv</code> might fail if the SV
123 isn't actually a string); thirdly there are more pleasant ways
124 to present this interface.
128 Writing a high-level wrapper around these low-level operations
129 is what is described in the rest of this document ...
132 <h2>Classes and constructors</h2>
135 Our general plan, therefore, will be to create an OCaml class
136 corresponding to <code>LWP::UserAgent</code>, which hides the
137 implementation (the <code>sv</code>, the calls to
138 <code>call_method</code>, and the conversion of arguments
139 to and from SVs). We will also need to write one or more constructor
144 We will write at least one method for every method exported by
145 the Perl interface. Sometimes we'll write two methods for each
146 Perl method, as in the case where a Perl method is a "getter/setter":
150 $ua->agent([$product_id])
151 Get/set the product token that is used to identify the user agent
152 on the network. The agent value is sent as the "User-Agent" header
157 becomes two methods in the OCaml version:
161 class lwp_useragent sv = object (self)
163 method agent : string
164 method set_agent : string -> unit
169 We will also write at least one function for every
170 constructor or static function exported from Perl.
174 The OCaml object itself contains the <code>sv</code> which
175 corresponds to the Perl SV (ie. the actual Perl object).
179 Here is the shape of our class:
183 (** Wrapper around Perl [LWP::UserAgent] class.
185 * Copyright (C) 20xx <i>your_organisation</i>
192 let _ = eval "use LWP::UserAgent"
194 class lwp_useragent sv = object (self)
196 <i>The methods will go here ...</i>
200 (* The "new" constructor. Note that "new" is a reserved word in OCaml. *)
203 let sv = call_class_method "LWP::UserAgent" "new" [<i>args ...</i>] in
206 <i>Any other static functions will go here ...</i>
210 Notice a few things here:
214 <li> There is some ocamldoc describing the class.
215 <li> We "open Perl" to avoid having to prefix everything with
217 <li> We <code>eval "use LWP::UserAgent"</code> when the module
218 is loaded. This is required by Perl.
219 <li> The <code>sv</code> (scalar value representing the actual
220 object) is passed as a parameter to the class.
223 <h2>Writing methods</h2>
225 <h3>Getters and setters</h3>
228 Of all types of methods, getters and setters are the easiest
229 to write. First of all, check the manual page to find out what
230 type the slot is. You'll need to write one get method and
231 one set method. (Rarely you'll find getters and setters which
232 are quasi-polymorphic, for instance they can take a string or
233 an arrayref. You'll need to think more deeply about these,
234 because they require one set method for each type, and the
235 get method can be complicated).
239 Here's our getter and setter for the agent slot, described above.
240 The agent is a string:
245 string_of_sv (call_method sv "agent" [])
247 call_method_void sv "agent" [sv_of_string v]
255 <li> The get method is just called <code>agent</code> (not
256 "get_agent"). This is the standard for OCaml code.
257 <li> We use <code>string_of_sv</code> and <code>sv_of_string</code>
258 to convert to and from SVs. This will ensure that the
259 class interface will have the correct type (string), and
260 thus be type safe as far as the calling code is concerned,
261 and also means the caller doesn't need to worry about
263 <li> The set method called <code>call_method_void</code> which
264 we haven't seen before. This is exactly the same as
265 <code>call_method</code> except that the method is called
266 in a "void context" - in other words, any return value is
267 thrown away. This is slightly more efficient than ignoring
272 Here's another example, with a boolean slot:
277 bool_of_sv (call_method sv "parse_head" [])
278 method set_parse_head v =
279 call_method_void sv "parse_head" [sv_of_bool v]
282 <h3>Ordinary methods</h3>
285 Other methods are perhaps simpler to wrap than getters and
286 setters. <code>LWP::UserAgent</code> contains an interesting
287 method called <code>request</code> (which actually runs the
292 What's particularly interesting about this method are the
293 parameter and return value. It takes an <code>HTTP::Request</code>
294 object and returns an <code>HTTP::Response</code>.
298 I have already wrapped <code>HTTP::Request</code> and
299 <code>HTTP::Response</code> as modules
300 <a href="../html/Pl_HTTP_Request.html">Pl_HTTP_Request</a> and
301 <a href="../html/Pl_HTTP_Response.html">Pl_HTTP_Response</a>
302 respectively. You should go and look at the code in those
307 If <code>request</code> requires a parameter, what should that
308 parameter be? Naturally it should be the SV corresponding to
309 the <code>HTTP::Request</code> object. To get this, I provided
310 an <code>#sv</code> method on the <code>http_request</code> class.
314 And what will <code>request</code> return? Naturally it will
315 return an SV which corresponds to the (newly created inside
316 Perl) <code>HTTP::Response</code> object. We need to wrap
317 this up and create a new OCaml <code>http_response</code> object,
318 <em>containing that SV</em>.
322 This is what the final method looks like:
326 method request (request : http_request) =
327 let sv = call_method sv "request" [request#sv] in
332 It's actually not so complicated.
335 <h2>Writing constructors and static functions</h2>
338 Constructors are fairly simple, although the <code>new_</code>
339 function inside <code>Pl_LWP_UserAgent</code> is complicated
340 by the many optional arguments which <code>LWP::UserAgent->new</code>
345 Here is the guts, omitting all but one of the optional args:
349 let new_ ?agent (* ... *) () =
351 let may f = function None -> () | Some v -> f v in
353 args := sv_of_string "agent" :: sv_of_string v :: !args) agent;
355 let sv = call_class_method "LWP::UserAgent" "new" !args in
360 It works simply enough, first building up a list of <code>sv</code>s
361 corresponding to the arguments, then calling
362 <code>call_class_method</code> to create the Perl object, then
363 returning a constructed OCaml <code>lwp_useragent</code> object
364 containing that <code>sv</code>.
367 <h2>Contributing wrappers back to perl4caml</h2>
370 If you write a wrapper for a Perl class, particularly one from
371 <a href="http://www.cpan.org/">CPAN</a>, I urge you to
372 contribute it back to the <a
373 href="http://www.merjis.com/developers/perl4caml/">perl4caml</a>
374 development effort. Your contribution enriches the project
375 as a whole, and makes OCaml more useful too.
379 <address><a href="mailto:rich@annexia.org">Richard W.M. Jones</a></address>
380 <!-- Created: Thu Oct 16 13:36:59 BST 2003 -->
382 Last modified: Thu Oct 16 14:39:02 BST 2003