<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="generator" content="AsciiDoc 8.3.4" /> <title>tpl Perl API</title> <link rel="stylesheet" href="./tdh.css" type="text/css" /> <link rel="stylesheet" href="./tdh-quirks.css" type="text/css" /> </head> <body> <div id="header"> <h1>tpl Perl API</h1> <span id="author">Troy D. Hanson</span><br /> <span id="email"><tt><<a href="mailto:troydhanson@comcast.net">troydhanson@comcast.net</a>></tt></span><br /> <span id="revision">version 1.1,</span> April 2007 </div> <div id="preamble"> <div class="sectionbody"> <a style="float: right;" href="http://sourceforge.net/projects/tpl"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=157637&type=16" width="150" height="40" alt="SourceForge.net" /></a> <div id="topnav" style="font-size: 9pt; font-family: sans-serif;"> <a style="padding: 8px;" href="http://sourceforge.net/projects/tpl/">sf.net summary page</a> > <a style="padding: 8px;" href="index.html">tpl home</a> > tpl Perl API <a style="padding: 8px;" href="userguide.pdf">[View PDF]</a> </div> </div> </div> <h2 id="_perl_api">Perl API</h2> <div class="sectionbody"> <div id="toc"></div> <script> window.onload=generate_TOC /* Author: Mihai Bazon, September 2002 * <a href="http://students.infoiasi.ro/~mishoo">http://students.infoiasi.ro/~mishoo</a> * * Table Of Content generator * Version: 0.4 * * Feel free to use this script under the terms of the GNU General Public * License, as long as you do not remove or alter this notice. */ /* modified by Troy D. Hanson, September 2006. License: GPL */ function H_getText(el) { var text = ""; for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 3 /* Node.TEXT_NODE, IE doesn't speak constants */) text += i.data; else if (i.firstChild != null) text += H_getText(i); } return text; } function TOC_EL(el, text, level) { this.element = el; this.text = text; this.level = level; } function getHeadlines(el) { var l = new Array; var rx = /[hH]([2-3])/; // internal recursive function that scans the DOM tree var rec = function (el) { for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 1 /* Node.ELEMENT_NODE */) { if (rx.exec(i.tagName)) l[l.length] = new TOC_EL(i, H_getText(i), parseInt(RegExp.$1)); rec(i); } } } rec(el); return l; } function generate_TOC() { var parent = document.getElementById("toc"); var toc_hdr = document.createElement("div"); var toc_hdr_txt = document.createTextNode("CONTENTS"); toc_hdr.appendChild(toc_hdr_txt); /* toc_hdr.setAttribute("id","hdr"); */ toc_hdr.id = "hdr"; parent.appendChild(toc_hdr); var hs = getHeadlines(document.getElementsByTagName("body")[0]); for (var i = 0; i < hs.length; ++i) { var hi = hs[i]; var d = document.createElement("div"); if (hi.element.id == "") hi.element.id = "gen" + i; var a = document.createElement("a"); a.href = "#" + hi.element.id; a.appendChild(document.createTextNode(hi.text)); d.appendChild(a); d.className = "level" + hi.level; parent.appendChild(d); /* if (hi.level == 3) { var dvtop = document.createElement("div"); dvtop.className = "toplink"; dvtop.appendChild(document.createTextNode("^top^")); dvtop.onclick=function(){scrollTo(0,0);}; hi.element.appendChild(dvtop); } */ } } </script> <div class="paragraph"><p>The Perl API for reading and writing tpl is nearly identical to the C API. This document will briefly explain the Perl API and provide examples. The chief motivation for having a Perl API is to communicate with C programs that use tpl.</p></div> <div class="admonitionblock"> <table><tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> <div class="title">Start with the C API</div>This document assumes familiarity with the C API. The concepts of using tpl are not explained here. For an introduction to tpl and its C API, see the <a href="userguide.html">User Guide</a>.</td> </tr></table> </div> <h3 id="_tpl_pm">Tpl.pm</h3><div style="clear:left"></div> <div class="paragraph"><p>The <tt>Tpl.pm</tt> file (in the <tt>lang/perl</tt>) directory contains the Perl module. You can copy it to another directory if you wish. Your Perl program may need to include a <tt>use lib</tt> statement to find the module.</p></div> <div class="literalblock"> <div class="content"> <pre><tt>#!/usr/bin/perl use lib "/some/directory"; use Tpl;</tt></pre> </div></div> <h3 id="_tpl_map">tpl_map</h3><div style="clear:left"></div> <div class="paragraph"><p>This function resembles the C version, except that it’s invoked via the <tt>Tpl</tt> module, and it takes references to Perl variables after the format string.</p></div> <div class="literalblock"> <div class="content"> <pre><tt>my $i; my $tpl = Tpl->tpl_map("A(i)",\$i);</tt></pre> </div></div> <div class="paragraph"><p>The return value is a tpl object; all other API calls are object methods. Incidentally, there is no <tt>tpl_free()</tt> method corresponding to the C API.</p></div> <h4 id="_fixed_length_arrays">Fixed-length arrays</h4> <div class="paragraph"><p>Format strings such as <tt>i#</tt> denote a fixed-length array. In the Perl API, fixed-length arrays require two arguments: a list reference, and the fixed length. For example:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>my @x; my $tpl = Tpl->tpl_map("i#", \@x, 10);</tt></pre> </div></div> <div class="paragraph"><p>When fixed-length arrays are packed or unpacked, the specified number of elements will be copied from (or placed into) the designated list.</p></div> <h4 id="_structures">Structures</h4> <div class="paragraph"><p>Format strings containing <tt>S(…)</tt> are handled in the Perl API as if only the interior, parenthesized part was present. (It does not work like the C API). So simply ignore the <tt>S(…)</tt> and consider only its interior format characters when constructing the argument list:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>my ($str, $int); my $tpl = Tpl->tpl_map("S(si)", \$str, \$int);</tt></pre> </div></div> <div class="paragraph"><p>It really only makes sense to use <tt>S(…)</tt> in a format string in the Perl API if you are communicating with a C program that uses structures.</p></div> <h3 id="_tpl_pack">tpl_pack</h3><div style="clear:left"></div> <div class="paragraph"><p>This is nearly identical to the C version. The only argument is the index number to pack.</p></div> <div class="literalblock"> <div class="content"> <pre><tt>$tpl->tpl_pack(1);</tt></pre> </div></div> <h3 id="_tpl_dump">tpl_dump</h3><div style="clear:left"></div> <div class="paragraph"><p>This method is a little different than the C version. Given no arguments, it returns the tpl image; given one argument it writes a file with that name.</p></div> <div class="literalblock"> <div class="content"> <pre><tt>$tpl->tpl_dump("demo.tpl"); # writes demo.tpl</tt></pre> </div></div> <div class="paragraph"><p>Or,</p></div> <div class="literalblock"> <div class="content"> <pre><tt>my $img = $tpl->tpl_dump();</tt></pre> </div></div> <div class="paragraph"><p>The tpl image is a binary buffer. You can do whatever you want with it, such as write it to a socket or pipe (probably to C program listening on the other end), or save it somewhere and later re-load it using <tt>tpl_load()</tt>.</p></div> <h3 id="_tpl_load">tpl_load</h3><div style="clear:left"></div> <div class="paragraph"><p>This method loads a tpl image from a file or from a Perl variable. It takes one argument. If it’s not a reference, it’s assumed to be a filename to load.</p></div> <div class="literalblock"> <div class="content"> <pre><tt>$tpl->tpl_load("demo.tpl");</tt></pre> </div></div> <div class="paragraph"><p>Otherwise, if the argument is a Perl reference, it’s construed as a variable containing the tpl image:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>$tpl->tpl_load(\$img);</tt></pre> </div></div> <div class="paragraph"><p>The method will <tt>die</tt> if the image is invalid or the file doesn’t exist. You can wrap it with <tt>eval</tt> to catch such errors:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>eval { $tpl->tpl_load(\$img); }; print "failed to load\n" if $@;</tt></pre> </div></div> <h3 id="_tpl_unpack">tpl_unpack</h3><div style="clear:left"></div> <div class="paragraph"><p>This is nearly identical to the C version. The only argument is the index number to unpack.</p></div> <div class="literalblock"> <div class="content"> <pre><tt>$tpl->tpl_unpack(1);</tt></pre> </div></div> </div> <h2 id="_examples">Examples</h2> <div class="sectionbody"> <h3 id="_integer_array">Integer array</h3><div style="clear:left"></div> <div class="listingblock"> <div class="title">Packing A(i) to file</div> <div class="content"> <pre><tt>#!/usr/bin/perl use strict; use warnings; use Tpl; my $i; my $tpl = Tpl->tpl_map("A(i)",\$i); for($i=0; $i<10; $i++) { $tpl->tpl_pack(1); } $tpl->tpl_dump("demo.tpl");</tt></pre> </div></div> <div class="listingblock"> <div class="title">Unpacking A(i) from file</div> <div class="content"> <pre><tt>#!/usr/bin/perl use strict; use warnings; use Tpl; my $j; my $tpl2 = Tpl->tpl_map("A(i)",\$j); $tpl2->tpl_load("demo.tpl"); while($tpl2->tpl_unpack(1) > 0) { print "$j\n"; }</tt></pre> </div></div> <h3 id="_message_passing">Message-passing</h3><div style="clear:left"></div> <div class="paragraph"><p>While the bulk of this example is socket handling, it demonstrates how you can use tpl as a message-passing format. In the real-world, you might have a C server and a Perl client, for example. In this example, we’ll code both a client and a server in Perl.</p></div> <div class="sidebarblock"> <div class="sidebar-content"> <div class="sidebar-title">A server that sums integers</div> <div class="paragraph"><p>Programming literature is rife with contrived examples so we will follow in that tradition. Our server will do no more than sum a list of integers. But in doing so it will demonstrate message passing adequately. Both its input (the integer array) and its output (an integer) are tpl images, passed over a TCP/IP socket.</p></div> </div></div> <h4 id="_server">Server</h4> <div class="paragraph"><p>The server waits for a connection from a client. When it gets one, it accepts the connection and immediately forks a child process to handle it. Then it goes back to waiting for another new connection.</p></div> <div class="paragraph"><p>The server child process handles the client by loading and unpacking the tpl image sent by the client (containing an array of integers). It calculates their sum and constructs a new tpl image containing the sum, which it sends back to the client.</p></div> <div class="listingblock"> <div class="title">Server</div> <div class="content"> <pre><tt>#!/usr/bin/perl use strict; use warnings; use IO::Socket::INET; use Tpl; $SIG{CHLD} = "IGNORE"; # don't create zombies our $port = 2000; sub handle_client { my $client = shift; undef $/; my $request = <$client>; # get request (slurp) # read input array, and calculate total my ($i,$total); my $tpl = Tpl->tpl_map("A(i)", \$i); eval { $tpl->tpl_load(\$request); }; die "received invalid tpl" if $@; $total += $i while $tpl->tpl_unpack(1) > 0; # formulate response and send my $tpl2 = Tpl->tpl_map("i", \$total); $tpl2->tpl_pack(0); my $response = $tpl2->tpl_dump(); print $client $response; close $client; } my $server = IO::Socket::INET->new(LocalPort => $port, Type => SOCK_STREAM, Reuse => 1, Listen => 10 ) or die "Can't listen on port $port: $!\n"; while (1) { my $client = $server->accept(); next unless $client; # new connection my $pid = fork; die "can't fork: $!\n" unless defined $pid; if ($pid > 0) { # parent close $client; } elsif ($pid == 0) { # child handle_client($client); exit(0); } } close ($server);</tt></pre> </div></div> <h4 id="_client">Client</h4> <div class="paragraph"><p>The client is a simpler program. It constructs the tpl image containing the integer array (taken from its command-line arguments), connects to the server and sends the tpl image to it, and then awaits the response tpl. The response containing the sum is loaded, unpacked and printed.</p></div> <div class="listingblock"> <div class="title">Client</div> <div class="content"> <pre><tt>#!/usr/bin/perl use strict; use warnings; use IO::Socket::INET; use Tpl; our $port = 2000; # construct tpl my $i; my $tpl = Tpl->tpl_map("A(i)",\$i); $tpl->tpl_pack(1) while ($i=shift @ARGV); my $request = $tpl->tpl_dump(); # send to server, get response my $socket = IO::Socket::INET->new("localhost:$port") or die "can't connect"; print $socket $request; shutdown($socket,1); # done writing (half-close) undef $/; my $response = <$socket>; # get reply (slurp) # decode response (or print error) my $total; my $tpl2 = Tpl->tpl_map("i", \$total); eval { $tpl2->tpl_load(\$response); }; die "invalid response\n" if $@; $tpl2->tpl_unpack(0); print "total is $total\n";</tt></pre> </div></div> <h4 id="_running_thise_example">Running thise example</h4> <div class="paragraph"><p>If the client and server programs are in <tt>client.pl</tt> and <tt>server.pl</tt>, then you can run the example by starting the server in one window:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>./server.pl</tt></pre> </div></div> <div class="paragraph"><p>Then run the client in another window. E.g.,</p></div> <div class="literalblock"> <div class="content"> <pre><tt>./client.pl 1 2 3 4 5</tt></pre> </div></div> <div class="paragraph"><p>The client runs and then exits, printing:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>total is 15</tt></pre> </div></div> <div class="paragraph"><p>You can re-run the client with different arguments. When done, type <tt>Ctrl-C</tt> in the server window to terminate it.</p></div> </div> <div id="footer"> <div id="footer-text"> Version 1.1<br /> Last updated 2009-04-30 21:22:12 EDT </div> </div> </body> </html>