Perl API
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.
Tip
|
Start with the C API 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
User Guide. |
Tpl.pm
The Tpl.pm
file (in the lang/perl
) directory contains the Perl module. You
can copy it to another directory if you wish. Your Perl program may need to
include a use lib
statement to find the module.
#!/usr/bin/perl
use lib "/some/directory";
use Tpl;
tpl_map
This function resembles the C version, except that it’s invoked via the Tpl
module, and it takes references to Perl variables after the format string.
my $i;
my $tpl = Tpl->tpl_map("A(i)",\$i);
The return value is a tpl object; all other API calls are object methods.
Incidentally, there is no tpl_free()
method corresponding to the C API.
Fixed-length arrays
Format strings such as i#
denote a fixed-length array. In the Perl API,
fixed-length arrays require two arguments: a list reference, and the fixed
length. For example:
my @x;
my $tpl = Tpl->tpl_map("i#", \@x, 10);
When fixed-length arrays are packed or unpacked, the specified number of elements will be copied from (or placed into) the designated list.
Structures
Format strings containing S(...)
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 S(...)
and consider only its interior format characters when
constructing the argument list:
my ($str, $int);
my $tpl = Tpl->tpl_map("S(si)", \$str, \$int);
It really only makes sense to use S(...)
in a format string in the Perl API if
you are communicating with a C program that uses structures.
tpl_pack
This is nearly identical to the C version. The only argument is the index number to pack.
$tpl->tpl_pack(1);
tpl_dump
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.
$tpl->tpl_dump("demo.tpl"); # writes demo.tpl
Or,
my $img = $tpl->tpl_dump();
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 tpl_load()
.
tpl_load
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.
$tpl->tpl_load("demo.tpl");
Otherwise, if the argument is a Perl reference, it’s construed as a variable containing the tpl image:
$tpl->tpl_load(\$img);
The method will die
if the image is invalid or the file doesn’t exist. You
can wrap it with eval
to catch such errors:
eval { $tpl->tpl_load(\$img); };
print "failed to load\n" if $@;
tpl_unpack
This is nearly identical to the C version. The only argument is the index number to unpack.
$tpl->tpl_unpack(1);
Examples
Integer array
#!/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");
#!/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";
}
Message-passing
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.
Server
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.
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.
#!/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);
Client
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.
#!/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";
Running thise example
If the client and server programs are in client.pl
and server.pl
, then
you can run the example by starting the server in one window:
./server.pl
Then run the client in another window. E.g.,
./client.pl 1 2 3 4 5
The client runs and then exits, printing:
total is 15
You can re-run the client with different arguments. When done, type Ctrl-C
in
the server window to terminate it.