On a typical UNIX computer I can compile and link that program with a command like:
cc -o hiworld.exe hiworld.c -lX11whereas if I used the GNU C compiler that would simply be:
gcc -o hiworld.exe hiworld.c -lX11(and you may very well need other -l and/or -L arguments depending on the details of your system, these will be discussed in the next lesson.)
hiworld.c has a very simple interface: it pops up with the 'Hello!' string in place, mouse <Button-1> clicks put 'Hi.' strings in place, and any keyboard keystroke stops the primitive event handler loop and exits the program.
It is amusing to note that the equivalent functionality of that roughly 73 line C program can, with Perl/Tk to the rescue, be written in about 10 lines:
#!/usr/bin/perl -w use Tk; my $m = MainWindow->new; my $c = $m -> Canvas(-height => 200, -width => 300,); $c -> pack; $c -> create('text', 40, 50, '-text' => "Hello World!"); $m -> bind('<Any-KeyPress>' => sub{exit}); $m -> bind('<Button-1>' => sub { $c -> create('text',$c->XEvent->x,$c->XEvent->y, -text => "Hi.") }); MainLoop;(Note that a tcl/tk version can be as few as 6 lines! Neither the perl/tk nor tcl/tk versions are really fair to the C version since it cannot even refresh the additional 'Hi.' strings on expose events the way the perl and wish scripts do automatically.)
hiworld : hiworld.c cc -o hiworld hiworld.c -lX11which would be invoked with "make hiworld" for example. It is considered much more fashionable to throw in a bunch of macros into one's Makefile's as in the following:
CC = cc X11 = -lX11 all : hiworld hiworld : hiworld.c $(CC) -o hiworld hiworld.c $(X11)which is still invoked via "make hiworld" even though it looks a little more complicated. Perl's ExtUtils::MakeMaker facility is much more adept at making Makefiles though, its use is discussed in the next lesson.
h2xs -A -n Howdyand note the creation of a new Howdy/ directory with several new files. With version 1.16 of h2xs the directory appears as follows:
-rw-r--r-- user 118 Changes -rw-r--r-- user 982 Howdy.pm -rw-r--r-- user 158 Howdy.xs -rw-r--r-- user 54 MANIFEST -rw-r--r-- user 371 Makefile.PL -rw-r--r-- user 650 test.plThe remainder of this lesson concerns itself with modifying (at least) four of the new files that were automatically generated: Howdy.xs, Howdy.pm, Makefile.PL, and test.pl.
For those of you curious about the above options passed to h2xs try taking a look through perldoc h2xs or tkpod h2xs. Note that -A specifies omission of the AutoLoader, and the -n option is given the package name we will be creating: Howdy.
One way to get our C Xlib code into Howdy would be to append (and slightly modify) the contents of hiworld.c to the (initially) blank stub of Howdy.xs (on Unix that would be something like cat hiworld.c >> Howdy.xs.) We edit Howdy.xs to add the required CODE: and OUTPUT: RETVAL statements, as well as to change function main(argv argc) to a funtion hiworld(argv argc) (clever name eh?). So that now the head of Howdy.xs now appears like:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include(note the function type of int, although void would have been just as appropriate.)#include MODULE = Howdy PACKAGE = Howdy int hiworld(argc, argv) int argc char argv CODE:
The tail of Howdy.xs now appears something like this:
XCloseDisplay(mydisplay); OUTPUT: RETVALNote the absence of a call to exit(0); that we had in hiworld.c, and an absence of the braces {} for a main since there is no longer a main (we are turning out a hiworld() subroutine call for perl after all and perl.c already has a main{}).
We turn our attention to the Howdy/Makefile.PL file and change the line that initially says:
'LIBS' => [''], # e.g., '-lm'to read something like what we needed to use to compile hiworld.c from the command line, like:
'LIBS' => ['-lX11'], # e.g., '-lm'better still would be something like what is in the Makfile.PL for Tk, as of Tk-b11.02 that would be:
'LIBS' => ["$xlib -lX11 -lpt -lsocket -lnsl -lm"],Hmm... we don't know what "$xlib" interpolates to, so let's keep it simple:
'LIBS' => ['-lX11 -lpt -lsocket -lnsl -lm -ldnet'],(The very observant will note that I also added a line to Howdy's Howdy/Makefile.PL that reads:
'dist' => { COMPRESS => "gzip -9f" },which is for make dist to make a nice compact distribution file.)
Before attempting to build our Howdy extension lets put in some non-trivial code into the test.pl file so that make test will be interesting. Since test # 1 is the loading via a use Howdy; statement let us add a second test so that we set last_test_to_print to 2 and add a couple of careful lines to test.pl:
eval '$retval = &Howdy::hiworld($foo,$bar);'; if ($@) {print "not ok 2: $@\n";} else {print "ok 2\n";}We can now type the following standard commands to build and install our Howdy::hiworld extension to perl:
perl Makefile.PL make make test make install(Note also, statically linked Howdy perl binaries can be made by running something like this:
make -f Makefile.aperl inst_perl MAP_TARGET=howdyperlafter make and before make test. Scripts written with a statically linked howdyperl will need to start with #!/path/to/howdyperl.)
After make install it is possible to run perl scripts that call the hiworld() subroutine in as little as three simple lines of perl:
#!/usr/bin/perl use Howdy; $return = &Howdy::hiworld($a, $b);For illustrative and testing purposes I have made a distribution copy of the Howdy "module" for perl, as it appears up to this point in the discussion.
#!/usr/bin/perl use Howdy; $return = &hiworld($a, $b);we get error messages like:
$return = &Howdy::hiworld($a, $b);which will complain under -w about $a and $b not being initialized. Let's re-write that sample use script as:
#!/usr/bin/perl -w use Howdy; my ($a, $b) = (0, 0); $return = &Howdy::hiworld($a, $b); print "\$return = $return, \$a = $a, \$b = $b\n";