Using Inline::CPP with wxPerl |
|
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 CharityThis 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
Notes:
Doables:
discuss using Image raw.
and exposing DC implementation.
History:
2002-Sep-23 Created.