Internals - Introduction to Wx XS Wrappers

From WxPerl Wiki
Jump to: navigation, search

Introduction

This page explains how the wxPerl wrapper for wxWidgets is constructed. It uses examples written using standard XS files and included C++ files. This is because the concepts described are often more transparent in those file formats. If writing a new wrapper for a class or perhaps updating an existing one you would almost certainly wish to use ExtUtils::XSpp as described in cpan ExtUtils::XSpp doc.

wxWidgets Library for Perl Programmers

C++ Inheritance and Virtual Methods

This section assumes that you are unfamiliar with C++ but would like to be able to interpret the C++ header files in wxWidgets with a view to creating XS wrappers yourself. It is also useful information needed to explain certain aspects of the wxPerl implementation. It is assumed you are familiar with Perl and basic object oriented concepts.

wxWidgets provides a library of C++ classes designed to allow C++ developers to create cross platform applications. In most cases a C++ developer would create their own classes deriving from wxWidgets base classes and override the base class virtual methods where required or desired.

The c++ header files for classes wxFinalThing that inherits from wxNextThing, and wxNextThing that inherits from wxBaseThing might look as follows.

class wxBaseThing
{
 public:
  wxBaseThing();
  virtual ~wxBaseThing() { printf("Goodbye from wxBaseThing"); }
  int GetSomething() { return 1 }
  virtual int GetSomethingElse() { return 1 }
}

class wxNextThing : public wxBaseThing
{
 public:
  wxNextThing();
  virtual ~wxNextThing() { printf("Goodbye from wxNextThing"); }
  int GetSomething() { return 2 }
  virtual int GetSomethingElse() { return 2 }
}

class wxFinalThing : public wxNextThing
{
 public:
  wxFinalThing ();
  virtual ~wxFinalThing() { printf("Goodbye from wxFinalThing"); }
  int GetSomething() { return 3 }
  virtual int GetSomethingElse() { return 3 }
}

Within the C++ library there may be some C++ code that creates a wxFinalThing object and assigns this to some container.

.....
wxFinalThing *mything = new wxFinalThing();
container->SetObject( mything );
  

Elsewhere there may be some C++ code that is written to access the interface of wxBaseThing and uses the same container to access the 'mything' set above. So it receives a wxFinalThing object, but accesses it through the wxBaseThing interface

....
wxBaseThing mybase = container->GetObject();
int something = mybase.GetSomething();         // the value of something is     1
int somethingelse = mybase.GetSomethingElse(); // the value of somethingelse is 3
....

Back in the code that has responsibility for the destruction of mything

.....
delete mything;
// we will see printed:
//   Goodbye from wxFinalThing
//   Goodbye from wxNextThing
//   Goodbye from wxBaseThing

The points to note are that with a virtual method, code that accesses the method through a base class interface - in our case wxBaseThing, will nevertheless call the implementation in the class at the top of the inheritance chain of the object. For a none virtual method we get the implementation for the interface we are accessing. As a special case, virtual destructors are automatically called for all classes in the inheritance chain, base class last.

It is possible to access the virtual methods of other interfaces in the inheritance chain by being explicit about the interface. For non virtual methods a simple cast would be used

 wxFinalThing *mything = new wxFinalThing();
 int mfinal = mything->GetSomethingElse();
 int mnext  = mything->wxNextThing::GetSomethingElse();
 int mbase  = mything->wxBaseThing::GetSomethingElse();
 int xfinal = mything->GetSomething();
 int xnext  = ((wxNextThing*)mything)->GetSomething();
 int xbase  = ((wxBaseThing*)mything)->GetSomething();

For a real example of these concepts you may look at the interface for wxWindow. All of the GUI classes in wxWidgets derive from wxWindow. All will provide their own implementations for some of the virtual methods.

C++ Method Parameters

If you are unfamiliar with C and C++ then the exact meaning of the parameters to C++ methods may be confusing.

 int mymethod(int param1, int& param2, int* param3);

In the declaration for mymethod, param1 is passed by value, param2 is passed by reference and param3 is a pointer to an int. In usage you might see something like:

int a = 1;
int b = 2;
int c = 3;

int d = mymethod(a, b, &c);

To make life interesting to someone with no C / C++ background, the meaning of the ampersand in the method declaration is somewhat different to its meaning in the body of the code. Used as a prefix in the code body, it means 'address of';

wxObject and wxClassInfo

wxWidgets provides a system of runtime type information for classes derived from wxObject using the wxClassInfo class. This is important within wxWidgets and very important within wxPerl.

For example, many methods within wxWidgets will return a C++ type of wxWindow. An example is

wxWindow* wxWindow::GetParent();

The wxWindow returned may actually be any class that derives from wxWindow, such as wxPanel or wxFrame. Within C++ you can write code that checks the wxClassInfo and enters a different code path accordingly. In the wxPerl wrapper for wxWindow::Getparent it is crucial that we bless the returned object into the correct Perl class. If the object returned is a wxFrame it would be fairly useless if we returned a Perl object of class Wx::Window. We need to return a Perl object of class Wx::Frame. We can achieve this by querying the wxClassInfo member of the wxWindow to find out what derived class it actually is.

We can only do this for classes that both derive from wxObject and use one of the available wxWidgets macros to implement the dynamic class methods and members. For classes that do not do both of these things we must adopt a different approach and simply always bless the returned object into the same predefined Perl class.

Introduction to wxPerl Wrappers

Much of the code in the wxPerl wrapper is concerned with synchronising the destruction of Perl objects with the destruction of the wxWidgets objects that they are associated with and preventing memory leaks that might occur if these associations were not synchronised.

Consider the following

{
   my $frame = wxTheApp->GetTopWindow();
   my $statusbar = $frame->GetStatusBar();
   my $ownstatusbar = Wx::StatusBar->new($frame, -1, ....);
}

Clearly the Perl objects $frame, $statusbar and $ownstatusbar fall out of scope outside the containing brackets and will be garbage collected sometime soon. But what happens to the wxWidgets objects these Perl objects were associated with?

In C++ it is up to the programmer to delete the objects they create. Normally delete is used for this as in 'delete myobject'. Consulting the wxWidgets documentation we find that wxWindow, and therefore all classes that derive from it, implements a 'Destroy' method that that the programmer should call to safely delete the object, rather than using 'delete' directly. We can also read that a wxWindow class will destroy all of its children when the parent wxWindow itself is destroyed.

For classes based on wxWindow (and other classes that implement a Destroy method) the wxPerl wrapper mirrors the wxWidgets documented behaviour exactly. The programmer must control object destruction. So in our contrived example, to prevent a memory leak we would need:

{
   my $frame = wxTheApp->GetTopWindow();
   my $statusbar = $frame->GetStatusBar();
   my $ownstatusbar = Wx::StatusBar->new($frame, -1, ....);
   $ownstatusbar->Destroy;
}

Wrapping classes derived from objects that implement a Destroy method is the easiest case to deal with in terms of synchronising destruction. We simply do nothing to any referenced C++ object when the Perl object goes out of scope. We leave the programmer to call $obj->Destroy at the appropriate time as described in the wxWidgets docs.

Although having to explicitly call Destroy isn't particularly Perlish, in practice this is not a major issue for programmers using the wxPerl wrapper. Most windows created are ultimately children of a Wx::Frame or Wx::Dialog class and their destruction is handled when the programmer destroys the parent object. There are particular common cases where all the code examples in Wx::Demo and elsewhere show that calling Destroy is necessary:

When using builtin dialog classes:

 my $dlg = Wx::SomeDialogClass->new(....)
 $dlg->ShowModal();
 ......
 $dlg->Destroy;
 

In the Close Event handler of your own classes deriving from Wx::Frame and Wx::Dialog

 sub OnEventClose {
     my($self, $event) = @_;
     .......
     $self->Destroy;
 }

When your code dynamically adds and removes child windows, from a parent window, the removed child windows should be destroyed.

For wxWidgets classes that don't implement a Destroy method, the C++ programmer is expected to call 'delete' on the object at the appropriate time. In wxPerl we attempt to wrap this in the most 'Perlish' manner possible. That is, when we can wrap the object so that C++ object destruction can be handled transparently to the Perl user, this is done. Otherwise (and in some cases in addition to) we implement a 'Destroy' method in the Perl class that the user may call to delete the associated C++ object explicitly.

wxPerl and XS Basics

XS code is of course a way of writing Perl XS modules. The XS code is parsed by ExtUtils::ParseXS and output as C code which is then compiled.

Taking the example once again of wxWindow::Getparent(), the XS code is contained within the Wx source in the file Window.xs and looks as follows:

 wxWindow*
 wxWindow::GetParent()

The C code produced by ExtUtils::ParseXS will look something like the following

XS_EUPXS(XS_Wx__Window_GetParent)
{
  dVAR; dXSARGS;
  if (items != 1)
    croak_xs_usage(cv,  "THIS");

  {
    wxWindow * THIS = (wxWindow *) wxPli_sv_2_object( aTHX_ ST(0), "Wx::Window" );
    wxWindow * RETVAL;
    RETVAL = THIS->GetParent();
    ST(0) = sv_newmortal();
    wxPli_object_2_sv( aTHX_ ST(0), RETVAL );
  }
  XSRETURN(1);
}

ExtUtils::ParseXS is able to produce this by use of typemaps. Passing values from Perl to C code and back is always a case of converting a Perl scalar to a C type for input values and a C type to a Perl scalar for output values. ExtUtils::ParseXS has built in typemaps for simple types.

Typemap Type O_WXOBJECT

We provide the typemaps for wxPerl types in our own typemap files. You can find the typemap for wxWindow* in the top level typemap file in the Wx source. You will also find a file typemap.tmpl which wxPerl uses to make defining typemaps simpler. The script script/make_typemap.pl is used to perform some simple substitution on typemap.tmpl and output the typemap file.

You will find the following definition of the typemap for wxWindow

wxWindow *  O_WXOBJECT

Toward the end of the typemap file you will find input and output definitions for O_WXOBJECT

INPUT
 O_WXOBJECT
  $var = (${(my $t=$type)=~s/^Wx_/wx/;\$t}) wxPli_sv_2_object( aTHX_ $arg, 
     ${(my $ntt=$ntype)=~s/^(?:const\s+)?(?:Wx_|wx)(.*?)(?:Ptr)?$/$1/g;$ntt=qq{\"Wx::$ntt\"};\$ntt} );

OUTPUT
 O_WXOBJECT
   wxPli_object_2_sv( aTHX_ $arg, $var );

It is probably simpler to look at the template in typmap.tmpl for the input definition

INPUT
 O_WXOBJECT
   $var = (CPP_CLASS) wxPli_sv_2_object( aTHX_ $arg, PERL_CLASS );

The input typmap uses to regexes to determine the C++ class and Perl Class. It may seem like stating the obvious but the Perl class name has a hard coded relationship to the wxWidgets class name. This is true elswhere in the Wx code too. Unless we rewrite sections of the code and provide the necessary typemaps, a wxWidgets class named wxSomething must have a corresponding Perl class named Wx::Something.

In the C code example, the lines containing wxPli_sv_2_object and wxPli_object_2_sv have been produced from the input and output typemaps for O_WXOBJECT. The rest of the code is the standard output of ExtUtils::ParseXS.

Taking a look at the output first, the Wx API function wxPli_object_2_sv has the following definition that can be found in the Wx source file cpp/helpers.cpp

SV* wxPli_object_2_sv( pTHX_ SV* var, const wxObject* object )

As might be guessed from the name, this function takes pointers to an SV and a wxObject and populates the SV with a reference to an object blessed into the correct Perl class. The function handles some special cases that are discussed later but in the basic case the Perl class returned is determined by querying the wxClassInfo member of the wxObject. In addition to populating the SV it also returns a pointer to it for convenient usage. Important points are that it can only work for classes derived from wxObject and it can therefore dynamically determine the Perl class to return.

Taking a look at the input, the Wx API function wxPli_sv_2_object has the following definition that can be found in the Wx source file cpp/helpers.cpp

void* wxPli_sv_2_object( pTHX_ SV* scalar, const char* classname ) 

This function can take a scalar referencing any type of wxWidgets class and convert it a wxWidgets class of a type derived from the Perl class name in classname. Of course, the scalar must actually reference a class of the type or derived from the type given in classname. Use of this function is not limited to classes derived from wxObject. We provide the class name in a function parameter so there is no requirement to query the object for its class type.

It is worth noting at this point that the method in our example, wxWindow::GetParent() and many other methods throughout the wxPerl wrappers, may return a hash based Perl object or a simple Perl object depending on the actual class of the wxWindow returned and how that window was created.

To return a hash based object for a type with a typemap of O_WXOBJECT, the object must have been created in your Perl code and the object class must have a specific wxPerl implementation normally only in place when there is a requirement to support the override of virtual methods in your Perl code. For example, Wx::SplashScreen->new(...) returns an object that derives from wxFrame, but it will not be a hash based object. This is due to the method by which wxPerl supports overriding virtual methods. There is further detail on how wxPerl implements these overrides at Overriding Virtual Methods.

The end result is that the C++ class that we implemented to handle virtual overrides holds a reference to the associated Perl object. When the C++ class is destroyed, the reference count on the Perl SV is decreased. This reference to the Perl object is used within the C++ class to check for overrides of virtual methods in Perl code.

The API function wxPli_object_2_sv also takes advantage of this. When converting a wxWidgets object to a Perl object, wxPli_object_2_sv will first check if the wxWidgets object has a perl self reference. If yes it will simply return the reference after increasing its ref count. In this way, if a call to wxWindow::GetParent is returning a window created in your Perl code, you will get back the same hash based object you created. It is retreived from the self reference container. The same method in some other instance or class may be returning a wxWindow based object created within the wxWidgets framework. In this case you will see a non-hash based object if your typemap is O_WXOBJECT. This should be transparent to your Perl code unless of course it relies on every instance returned being a hash based object.

Typemap Type O_WXEVTHANDLER

There is a second basic typemap type where the wxWidgets class can contain a reference to the associated Perl object and in this case the underlying implemenation does not require any specialised C++ class implementation in wxPerl. This is where the typmap is O_WXEVTHANDLER. In wxWidgets any class that derives from wxEvtHandler has access to a client data storage member, The contents of this member are deleted on destruction of the owning object. Therefore we can store an object in that container that holds a reference to a Perl object. When the wxWidgets object is destroyed, the ref count on the Perl object is decreased. We don't have to do anything more to manage synchronisation between Perl objects and associated wxWidgets objects.

In the constructors of classes that have a typemap of O_WXEVTHANDLER there must be a call to the Wx API function

SV* wxPli_create_evthandler( pTHX_ wxEvtHandler* object, const char* classname )

This is needed to create the hash based object and initialise the wxEvtHandler client data container with it. The function is safe to call even if the constructor has also created a SelfRef as in the wxPliFrame. wxPli_create_evthandler will check if a self ref exists and use a reference to the same underlying Perl object if it does. If not it will call wxPli_make_object to create a completely new reference.

The API function wxPli_object_2_sv understands that some objects derived from wxEvtHandler may contain a Perl reference and in fact a pseudo code representation of what wxPli_object_2_sv does is:

if object->isa wxEvtHandler and object->HasEvtHandlerPerlObj
   return object->GetEvtHandlerPerlObj;
   
if object->HasSelfRef
	return object->GetSelfRef;
 
// default

 get classtype from wxClassInfo
 return object blessed into classtype