Using Inline::CPP with wxPerl

Mitchell Charity
  -   Inline.pm Notes

Date: Fri, 13 Sep 2002 15:39:27 -0400
Message-Id: <200209131939.g8DJdR604755@vendian.org>
To: wxperl-users@lists.sourceforge.net, inline@perl.org
Cc: wx-users@lists.wxwindows.org
Subject: Using Inline::CPP with wxPerl
From: Mitchell N Charity 

This is a quick how-to-get-started note on using Inline::CPP with wxPerl.

  wxPerl is a Perl wrapper for the popular wxWindows C++ GUI toolkit.

  The Inline and Inline::CPP (C++) perl modules allow you to put
  C++ source code directly in Perl source.  Glue code generation,
  compilation (cached), and dynamic linking are handled automagically.
  They can also be used from Python via Zope-Perl.

However, it is _not_ clear one should _want_ to use Inline with wxPerl.
Inline is still rather immature, wxPerl is already fast, and I've seen
only low-order 10x speedups.  But there are still some applications.
And you might simply wish to play.  If so, this note might save you
perhaps an hour or so.

Here are the parts we'll need:

  Inline and Inline::CPP are available from CPAN as usual.
  Here is documentation,
    http://search.cpan.org/author/INGY/Inline-0.43/Inline.pod
    http://search.cpan.org/author/NEILW/Inline-CPP-0.24/CPP.pod
  and a mailinglist
    http://www.mail-archive.com/inline%40perl.org/

  wxPerl is also available from CPAN, and from its homepage
  http://wxperl.sourceforge.net/ .  It too has a mailinglist
  http://www.geocrawler.com/lists/3/SourceForge/8008/0/ .
  Before installing wxPerl, one must first install wxWindows
  http://www.wxwindows.org/ .

I used
  wxWindows GTK 2.2.9 (from sources, but that shouldn't be necessary),
  wxPerl Wx-0.11 (keep the sources, we'll need them),
  Inline 0.43,
  Inline::CPP 0.23 (the current 0.24 should be fine).

Just so we are clear, before you invest a lot of time, my conflation
of these, which I'm about to sketch, might charitably be described as
an untested toy.  But they are themselves nifty packages, and arguably
worth having in their own rights.

So, you've installed all of these, the Inline automated tests went
fine, the wxPerl Wx-0.11/demo/demo.pl works (more or less), as do
examples from the Inline::CPP manpage.

Ok, our task is to take some things from the wxPerl installation,
which it normally doesn't hand out, and then give them to Inline.

We are about to create various files and directories.  Their locations
are arbitrary.  I'll suggest some plausible installation-ish locations,
but you could drop them all in /tmp/plaything/ and be fine.

(1) Find where the wxPerl (Wx-0.11) installation installed Wx.so .
    Someplace like
    /usr/local/lib/perl5/site_perl/5.6.1/i686-linux/auto/Wx/Wx.so
    Create a libWx.so symbolic link to it (ln -s Wx.so libWx.so) so we can
    dynamically load it in later.  Remember the directory of libWx.so (#1).

(2) Copy Wx-0.11/typemap from the Wx-0.11 sources to someplace like
    /usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/typemap
    Someday wxPerl will do this itself.  Remember the filename (#2).

(3) We'll need some of the Wx-0.11 header files.  You could just
    leave them as-is in the source directory.  I'll create a
    /usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/include
    and copy Wx-0.11/cpp/ there.  So now I have a
    /usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/include/cpp/
    with a few .h files.  Remember the include/ directory (#3).

(4) Open the Wx-0.11/GDI.xs source file.  We need a copy of the
    (regrettably extensive) preprocessor magic needed to use the
    wxPerl glue code from C++.  Pause to appreciate the folly of
    copying application-internal code which may change with each
    new version of Wx.
(4a) Copy the preprocessor stuff from the top, down to #undef THIS.
     Ie, just before the first Module= declaration, but not including
     the WXPLI_BOOT_ONCE or wxPli_boot_Wx_GDI lines.
(4b) Add  #define _WXP_DEFINE_CLASSNAME 1
     just before the #include "cpp/typedef.h".
     Your result should look something like the example attached below.
(4c) Save this someplace.  I'll call it wx_inline.h, and put it in
     /usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/include/
     so we don't have to keep track of yet another directory.

Ok, that is it for the wxPerl side.  Whew.

(Though I probably forgot something, as I didn't scrub down my system
 an rebuild... apologies in advance...)

Now, all we need to do, is to feed this accumulated information, and a
bit more, to Inline.

The basic approach is

BEGIN {
  $wx_libWx_directory =
    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/auto/Wx/"; #1
  $wx_typemap_filename =
    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/typemap"; #2
  $wx_include_directory =
    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/include/"; #3
  chomp($wx_config_libs     = `wx-config --libs`);
  chomp($wx_config_cxxflags = `wx-config --cxxflags`);
};
use Inline (CPP => Config =>
	    LIBS => "-L$wx_libWx_directory -lWx",
	    TYPEMAPS => $wx_typemap_filename,
	    ALTLIBS => $wx_config_libs,
	    CCFLAGS => ($wx_config_cxxflags ." -I$wx_include_directory")
	    );

Then we can write things like

use Inline CPP => <<'ENDMARKER';
#include "wx_inline.h"
#include 
#include 
void foo(Wx_Frame* f) {
    Wx_DC* dc = new Wx_PaintDC(f);
    int i;
    for(i=0;i<20000000;i++) {
	dc->DrawEllipse( 20, 20, 50, 50 );
    }
}
ENDMARKER

and call them normally in perl

  $frame = SomeFrame->new();
  &foo($frame);

And that is all there is to it.

You can now write wxWindows C++ code in your perl files, and apply it
to wxPerl objects.

That sentence gives me the shivers.  Hey kids, you can use shaped
charges to open cans of worms.  Please note, we have taken one layer
of foreign function interface glue code, traditionally one of the most
gotcha-laden areas of software engineering, to a GUI no less, and are
now cheerfully troweling in another layer of it.  I encourage an
effort to remember that premature optimization perhaps belongs up
there with stupidity as a mortal sin.  'Nuff said.

Some asides:

 The BEGIN was needed so the variables would be available when perl
 is starting up, at perl "compiletime", which is when Inline code
 will usually be built.  (Though Inline works just fine at "runtime"
 too, so one can do dynamic code generation!  Which I discuss on
 http://www.vendian.org/mncharity/dir3/inline/ :).

 Wx_Whatever is wxPerl's typedef'ed alias for wxWindows's Wx::Whatever.

 You may find this perl invocation useful when debugging Inline code:
   perl -MInline=FORCE,INFO,NOCLEAN -w filename.pl
 It will FORCE (re)compilation even if there is a cached version
 (Inline doesn't yet hash on library and compiler argument
  information, so the cache can become invalid during development),
 provide INFO on the compile, and with NOCLEAN, will leave the
 "temporary" build files behind, even after a successful compilation,
 so you can see what was built.
 Also, fyi, you will likely be repeatedly looking at
   ./_Include/build/mumblemumble/out.make

 Don't draw an ellipse 20M times if its target is already being shown.
 You'll get an window system update each time.  Weeks later...
 Before being shown, it might only take 5 sec, vs 35 s for pure wxPerl.
 
 End of asides.


One place to get started is with Wx-0.11/samples/hello/hello.pl .
Hello world.  And then twiddle it with Inline.  An example is
attached below.

Enjoy,
Mitchell Charity
(hey, it's friday...)

---- $ diff -U 3 hello.pl hey.pl ---- -----------------------
--- hello.pl    Sun Mar  4 04:41:46 2001
+++ hey.pl      Fri Sep 13 14:13:22 2002
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/perl -w
 #############################################################################
 ## Name:        hello.pl
 ## Purpose:     Hello wxPerl sample
@@ -46,6 +46,26 @@
 use Wx qw(wxDefaultPosition);
 use Wx qw(wxWHITE);
 
+
+no strict;
+BEGIN {
+  $wx_libWx_directory =
+    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/auto/Wx/"; #1
+  $wx_typemap_filename =
+    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/typemap"; #2
+  $wx_include_directory =
+    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/include/"; #3
+  chomp($wx_config_libs     = `wx-config --libs`);
+  chomp($wx_config_cxxflags = `wx-config --cxxflags`);
+};
+use Inline (CPP => Config =>
+           LIBS => "-L$wx_libWx_directory -lWx",
+           TYPEMAPS => $wx_typemap_filename,
+           ALTLIBS => $wx_config_libs,
+           CCFLAGS => ($wx_config_cxxflags ." -I$wx_include_directory")
+           );
+use strict;
+
 sub new {
   # new frame with no parent, id -1, title 'Hello, world!'
   # default position and size 350, 100
@@ -74,8 +94,22 @@
   $dc->SetFont( $this->font );
   # darw a friendly message
   $dc->DrawText( 'Hello, world!', 10, 10 );
+
+  # draw an ellipse, using an Inline generated method.
+  $this->frobit($dc);
 }
 
+use Inline CPP => <<'ENDMARKER';
+#include "wx_inline.h"
+#include 
+#include 
+void frobit(Wx_Frame* f, Wx_DC* dc) {
+    Wx_DC* an_illustrative_extra_dc = new Wx_PaintDC(f);
+    dc->DrawEllipse( 20, 20, 50, 50 );
+}
+ENDMARKER
+
+
 sub font {
   $_[0]->{FONT};
 }
---- end ---- -----------------------------------------------

---- hey.pl ---- --------------------------------------------
#!/usr/bin/perl -w
#############################################################################
## Name:        hello.pl
## Purpose:     Hello wxPerl sample
## Author:      Mattia Barbon
## Modified by:
## Created:      2/11/2000
## RCS-ID:      
## Copyright:   (c) 2000 Mattia Barbon
## Licence:     This program is free software; you can redistribute it and/or
##              modify it under the same terms as Perl itself
#############################################################################

use strict;
use Wx;

# every program must have a Wx::App-derive class
package MyApp;

use vars qw(@ISA);

@ISA = qw(Wx::App);

# this is called automatically on object creation
sub OnInit {
  my( $this ) = shift;

  # create a new frame
  my( $frame ) = MyFrame->new();

  # set as top frame
  $this->SetTopWindow( $frame );
  # show it
  $frame->Show( 1 );
}

package MyFrame;

use vars qw(@ISA);

@ISA = qw(Wx::Frame);

use Wx::Event qw(EVT_PAINT);
# this imports some constants
use Wx qw(wxDECORATIVE wxNORMAL wxBOLD);
use Wx qw(wxDefaultPosition);
use Wx qw(wxWHITE);


no strict;
BEGIN {
  $wx_libWx_directory =
    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/auto/Wx/"; #1
  $wx_typemap_filename =
    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/typemap"; #2
  $wx_include_directory =
    "/usr/local/lib/perl5/site_perl/5.6.1/i686-linux/Wx/include/"; #3
  chomp($wx_config_libs     = `wx-config --libs`);
  chomp($wx_config_cxxflags = `wx-config --cxxflags`);
};
use Inline (CPP => Config =>
	    LIBS => "-L$wx_libWx_directory -lWx",
	    TYPEMAPS => $wx_typemap_filename,
	    ALTLIBS => $wx_config_libs,
	    CCFLAGS => ($wx_config_cxxflags ." -I$wx_include_directory")
	    );
use strict;

sub new {
  # new frame with no parent, id -1, title 'Hello, world!'
  # default position and size 350, 100
  my( $this ) = shift->SUPER::new( undef, -1, 'Hello, world!',
                                   wxDefaultPosition , [350, 100] );

  # create a new font object and store it
  $this->{FONT} = Wx::Font->new( 40, wxDECORATIVE, wxNORMAL, wxBOLD, 0 );
  # set background colour
  $this->SetBackgroundColour( wxWHITE );

  $this->SetIcon( Wx::GetWxPerlIcon() );

  # declare that all paint events will be handled with the OnPaint method
  EVT_PAINT( $this, \&OnPaint );

  return $this;
}

sub OnPaint {
  my( $this, $event ) = @_;
  # create a device context (DC) used for drawing
  my( $dc ) = Wx::PaintDC->new( $this );

  # select the font
  $dc->SetFont( $this->font );
  # darw a friendly message
  $dc->DrawText( 'Hello, world!', 10, 10 );

  # draw an ellipse, using an Inline generated method.
  $this->frobit($dc);
}

use Inline CPP => <<'ENDMARKER';
#include "wx_inline.h"
#include 
#include 
void frobit(Wx_Frame* f, Wx_DC* dc) {
    Wx_DC* an_illustrative_extra_dc = new Wx_PaintDC(f);
    dc->DrawEllipse( 20, 20, 50, 50 );
}
ENDMARKER


sub font {
  $_[0]->{FONT};
}

package main;

# create an instance of the Wx::App-derived class
my( $app ) = MyApp->new();
# start processing events
$app->MainLoop();

# Local variables: #
# mode: cperl #
# End: #
---- end ---- -----------------------------------------------

---- $ perl -w -MInline=FORCE,INFO hey.pl ---- --------------
<-----------------------Information Section----------------------------------->

Information about the processing of your Inline CPP code:

Your source code needs to be compiled. I'll use this build directory:
/a/Stable/home/mcharity/wx/_Inline/build/MyFrame_2e7a

and I'll install the executable as:
/a/Stable/home/mcharity/wx/_Inline/lib/auto/MyFrame_2e7a/MyFrame_2e7a.so

No C++ classes have been successfully bound to Perl.

The following C++ functions have been bound to Perl:
        void frobit(Wx_Frame * f, Wx_DC * dc);


<-----------------------End of Information Section---------------------------->
---- end ---- -----------------------------------------------

--- wx_inline.h ---- ----------------------------------------
// Extracted from Wx-0.11/GDI.xs
// Changed as indicated.
#undef bool
#define PERL_NO_GET_CONTEXT

#include 
#include 

#include "cpp/compat.h"
#include "cpp/chkconfig.h"

WXPL_EXTERN_C_START
#include 
#include 
#include 
WXPL_EXTERN_C_END

#undef bool
#undef Move
#undef Copy
#undef New
#undef Pause
#if defined( __WXMSW__ )
#undef read
#undef write
#undef eof
#undef form
#undef vform
#endif

#if __VISUALC__
#pragma warning (disable: 4800 )
#endif

#ifdef __WXMSW__
#include 
#endif // __WXMSW__

// some helper functions/classes/macros
#define _WXP_DEFINE_CLASSNAME 1 // ############ for Inline
#include "cpp/typedef.h"
#include "cpp/helpers.h"

#undef THIS
---- end ---- -----------------------------------------------

END OF MESSAGE


Comments welcome - Mitchell Charity <mcharity@vendian.org>

Notes:

Doables:
 discuss using Image raw. 
  and exposing DC implementation.

History:
 2002-Sep-23  Created.