/usr/share/doc/libperl4caml-ocaml-doc/writing-a-wrapper.html is in libperl4caml-ocaml-doc 0.9.5-4build10.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Writing a high-level wrapper around a Perl library</title>
<link rel="stylesheet" href="http://www.merjis.com/css/default.css"
type="text/css">
</head>
<body bgcolor="#ffffff">
<h1>Writing a high-level wrapper around a Perl library</h1>
<p>
This document discusses the theory and practice behind writing
a wrapper around a typical object-oriented Perl library. We
use <a href="../html/Pl_LWP_UserAgent.html">Pl_LWP_UserAgent</a>
as an example. (This is the high-level wrapper around the
<a href="http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm">LWP::UserAgent</a>
library).
</p>
<p>
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
<a href="http://www.merjis.com/developers/perl4caml/">perl4caml</a>
development.
</p>
<h2>First steps</h2>
<p>
I'm going to use <a href="http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm">LWP::UserAgent</a>
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:
</p>
<pre>
perldoc LWP::UserAgent
</pre>
<p>
or <a href="http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm">follow
this link</a>.
</p>
<h2>Understanding what we're doing</h2>
<p>
The low-level <a href="../html/Perl.html">Perl</a> module offers
two useful functions and a useful datatype which we'll be using
extensively. The useful functions are:
</p>
<table class="table">
<tr>
<th> Function name </th>
<th> Perl equivalent </th>
<th> Description </th>
</tr>
<tr>
<td> <code>call_class_method</code> </td>
<td> <code>$obj = LWP::UserAgent->new (args...)</code> </td>
<td>
<p> Calls a static method or constructor on a class. </p>
</td>
</tr>
<tr>
<td> <code>call_method</code> </td>
<td> <code>$obj->some_method (args...)</code> </td>
<td>
<p> Calls an instance method on an object. </p>
</td>
</tr>
</table>
<p>
The useful datatype is called the <code>Perl.sv</code> (an
abstract type), which represents a scalar value in Perl (anything
you would normally write in Perl with a <code>$</code>, including
numbers, strings, references and blessed objects). To find out
more about "SVs" see <a href="http://www.perldoc.com/perl5.8.0/pod/perlguts.html">the perlguts(3) man page</a>.
</p>
<p>
To see how these three things interact, let's create an
<code>LWP::UserAgent</code> object and call a method on it:
</p>
<pre>
# #load "perl4caml.cma";;
# open Perl;;
# let sv = call_class_method "LWP::UserAgent" "new" [];;
<i>val sv : Perl.sv = <abstr></i>
# let agent = call_method sv "agent" [];;
<i>val agent : Perl.sv = <abstr></i>
# string_of_sv agent;;
<i>- : string = "libwww-perl/5.69"</i>
</pre>
<p>
Note how the variable <code>sv</code> contains the actual Perl
object (an instance of <code>LWP::UserAgent</code>). To be
quite clear, this is the equivalent of the following Perl code:
</p>
<pre>
$sv = LWP::UserAgent->new ();
$agent = $sv->agent ();
print $agent;
</pre>
<p>
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 <code>string_of_sv</code> might fail if the SV
isn't actually a string); thirdly there are more pleasant ways
to present this interface.
</p>
<p>
Writing a high-level wrapper around these low-level operations
is what is described in the rest of this document ...
</p>
<h2>Classes and constructors</h2>
<p>
Our general plan, therefore, will be to create an OCaml class
corresponding to <code>LWP::UserAgent</code>, which hides the
implementation (the <code>sv</code>, the calls to
<code>call_method</code>, and the conversion of arguments
to and from SVs). We will also need to write one or more constructor
function.
</p>
<p>
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":
</p>
<pre>
$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.
</pre>
<p>
becomes two methods in the OCaml version:
</p>
<pre>
class lwp_useragent sv = object (self)
(* ... *)
method agent : string
method set_agent : string -> unit
end
</pre>
<p>
We will also write at least one function for every
constructor or static function exported from Perl.
</p>
<p>
The OCaml object itself contains the <code>sv</code> which
corresponds to the Perl SV (ie. the actual Perl object).
</p>
<p>
Here is the shape of our class:
</p>
<pre>
(** Wrapper around Perl [LWP::UserAgent] class.
*
* Copyright (C) 20xx <i>your_organisation</i>
*
* $ Id $
*)
open Perl
let _ = eval "use LWP::UserAgent"
class lwp_useragent sv = object (self)
<i>The methods will go here ...</i>
end
(* The "new" constructor. Note that "new" is a reserved word in OCaml. *)
let new_ ... =
...
let sv = call_class_method "LWP::UserAgent" "new" [<i>args ...</i>] in
new lwp_useragent sv
<i>Any other static functions will go here ...</i>
</pre>
<p>
Notice a few things here:
</p>
<ol>
<li> There is some ocamldoc describing the class.
<li> We "open Perl" to avoid having to prefix everything with
<code>Perl.</code>.
<li> We <code>eval "use LWP::UserAgent"</code> when the module
is loaded. This is required by Perl.
<li> The <code>sv</code> (scalar value representing the actual
object) is passed as a parameter to the class.
</ol>
<h2>Writing methods</h2>
<h3>Getters and setters</h3>
<p>
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).
</p>
<p>
Here's our getter and setter for the agent slot, described above.
The agent is a string:
</p>
<pre>
method agent =
string_of_sv (call_method sv "agent" [])
method set_agent v =
call_method_void sv "agent" [sv_of_string v]
</pre>
<p>
Note:
</p>
<ol>
<li> The get method is just called <code>agent</code> (not
"get_agent"). This is the standard for OCaml code.
<li> We use <code>string_of_sv</code> and <code>sv_of_string</code>
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.
<li> The set method called <code>call_method_void</code> which
we haven't seen before. This is exactly the same as
<code>call_method</code> 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.
</ol>
<p>
Here's another example, with a boolean slot:
</p>
<pre>
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]
</pre>
<h3>Ordinary methods</h3>
<p>
Other methods are perhaps simpler to wrap than getters and
setters. <code>LWP::UserAgent</code> contains an interesting
method called <code>request</code> (which actually runs the
request).
</p>
<p>
What's particularly interesting about this method are the
parameter and return value. It takes an <code>HTTP::Request</code>
object and returns an <code>HTTP::Response</code>.
</p>
<p>
I have already wrapped <code>HTTP::Request</code> and
<code>HTTP::Response</code> as modules
<a href="../html/Pl_HTTP_Request.html">Pl_HTTP_Request</a> and
<a href="../html/Pl_HTTP_Response.html">Pl_HTTP_Response</a>
respectively. You should go and look at the code in those
modules now.
</p>
<p>
If <code>request</code> requires a parameter, what should that
parameter be? Naturally it should be the SV corresponding to
the <code>HTTP::Request</code> object. To get this, I provided
an <code>#sv</code> method on the <code>http_request</code> class.
</p>
<p>
And what will <code>request</code> return? Naturally it will
return an SV which corresponds to the (newly created inside
Perl) <code>HTTP::Response</code> object. We need to wrap
this up and create a new OCaml <code>http_response</code> object,
<em>containing that SV</em>.
</p>
<p>
This is what the final method looks like:
</p>
<pre>
method request (request : http_request) =
let sv = call_method sv "request" [request#sv] in
new http_response sv
</pre>
<p>
It's actually not so complicated.
</p>
<h2>Writing constructors and static functions</h2>
<p>
Constructors are fairly simple, although the <code>new_</code>
function inside <code>Pl_LWP_UserAgent</code> is complicated
by the many optional arguments which <code>LWP::UserAgent->new</code>
can take.
</p>
<p>
Here is the guts, omitting all but one of the optional args:
</p>
<pre>
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
</pre>
<p>
It works simply enough, first building up a list of <code>sv</code>s
corresponding to the arguments, then calling
<code>call_class_method</code> to create the Perl object, then
returning a constructed OCaml <code>lwp_useragent</code> object
containing that <code>sv</code>.
</p>
<h2>Contributing wrappers back to perl4caml</h2>
<p>
If you write a wrapper for a Perl class, particularly one from
<a href="http://www.cpan.org/">CPAN</a>, I urge you to
contribute it back to the <a
href="http://www.merjis.com/developers/perl4caml/">perl4caml</a>
development effort. Your contribution enriches the project
as a whole, and makes OCaml more useful too.
</p>
<hr>
<address><a href="mailto:rich@annexia.org">Richard W.M. Jones</a></address>
<!-- Created: Thu Oct 16 13:36:59 BST 2003 -->
<!-- hhmts start -->
Last modified: Thu Oct 16 14:39:02 BST 2003
<!-- hhmts end -->
</body>
</html>
|