Wrapping Existing Perl Modules

One of the most powerful features of PerlNET is the ability to make the existing Perl packages available to the .NET environment. This feature may be applied to Perl modules that were downloaded from CPAN or to any others. Whenever you decide to provide Perl module functionality to .NET, you should create a .NET component using PerlNET that will expose its class interface. This interface should define all the functionality that you wish to be available to .NET applications. Actually, this way we create a wrapper class for the Perl module. The role of this class is to readdress .NET calls to the corresponding module subroutines.

There are two major types of modules:

  • Library of subroutines

  • Classes

By library of subroutines, we mean modules that offer subroutines unified under the same topic, but do not represent any class module—that is, should not be instantiated with the new method prior to usage. We saw the sample Roman.pm for such a module type in Chapter 8. When programming for .NET, we must define a class. Therefore, for this type of module, we create a class with all static methods and without a constructor, as we should not instantiate objects for such class.[7]

[7] PerlNET will create the default constructor for the class, which will do nothing.

If we are going to wrap a class module, then we define nonstatic methods and properties in our PerlNET class interface. In both cases, the [interface: pure] attribute has to present inside the interface definition as we deal with the Core Perl modules.

In this section, we will show one sample of the wrapping for each module type. The Roman module will serve as the example of Library of subroutines. We will take the Whitespace module from CPAN, which cleans up bogus spaces, as our class module example.

Library of Subroutines Sample

We present the sample for wrapping the Roman module, which was described in Chapter 8. One of the rules of wrapping is to give the same package name and namespace as of the original module to our PerlNET package for a .NET component. Therefore, the Roman method, which translates the arabic numbers into roman numbers with the uppercase letters, should have a different name. Otherwise, it will be considered as the constructor for the Roman class, and this is not what we want. Hence, in the interface definition, we declare the romanUC method. The subroutine for this method readdresses the call to the original Roman function. You must require the Roman module after the interface definition. Here is the code with the renamed Roman method (we placed the sample in the WrappingRoman folder).


#
# Roman.pm
#
package Roman;

use strict;

=for interface
   [interface: pure]
   static str roman(int ar);
   static str romanUC(int ar);
   static bool isroman(str rom);
   static int arabic(str rom);
=cut

require Roman;

sub romanUC
							{
							return Roman::Roman(shift);
							}
						

Class Sample

Here is how wrapping is done for the Whitespace class module from CPAN (you may find the code in WrappingWhiteSpace).


#
# Whitespace.pm
#
package Whitespace;

use strict;

=for interface
   [interface: pure]
   static Whitespace(str infile);
   static Whitespace(str infile, str outfile);
   int detect();
   int cleanup();
   wantarray! str[] Status();
   str error();
   int leadclean();
   int trailclean();
   int indentclean();
   int spacetabclean();
   int eolclean();
=cut

require Whitespace;

sub Status
{
   my $self = shift;
   my @arr = %{$self->status()};
   return @arr;
}

When wrapping a Perl module, we have the freedom to decide which methods to expose to .NET and which not to. This is why we did not include all the methods enumerated in the Whitespace class module. When wrapping the Whitespace class module, we had to perform some workaround on the status method. This method returns a hash with counters for all whitespaces that were detected. The .NET environment does not “know” how to handle Perl hashes, so we cannot use the returned hash. However, we can convert any hash into a list (array) of strings. We implant our function Status into class interface (the function should have a different name from module function—changing case of the first letter is enough). In the body of the Status function, we call the original status and assign the return value to an array, which is returned to the .NET environment. Since Status returns an array, we have to add the wantarray! method modifier in the function definition.

wantarray! str[] Status();

The Whitespace class can be initialized with one or two filename parameters; hence, we overload the constructor. Before the definition of the constructors, we write the [interface: pure] attribute.

Using Wrapped Components in C#

Here we present a short C# client program that uses the Whitespace wrapped module to perform the clean-up operations on the input.txt file.



//
// WhitespaceCli.cs
using System;

public class WhitespaceCli
{
   public static void Main(string[] args)
   {
      // construct object
      Whitespace ws = new Whitespace("input.txt",
                                     "output.txt");

      // Detect bogus whitespaces
      int det = ws.detect();
      string err = ws.error();

      // Check if there was an error
      if (err != null)
      {
         Console.WriteLine(err);
         return;
      }
      Console.Write("{0} bogus whitespaces found
",
                                                det);
      if (det > 0)
      {
         // Output information
         // about detected whitespaces
         string[] stat = ws.Status();
         foreach (string s in stat)
         {
            Console.WriteLine(s);
         }

         // Perform input file clean-up
         ws.cleanup();
      }
   }
}

The code for this program is located in the WrappingWhitespaceCli folder under the chapter samples directory. In addition, see the WrappingRomanCli folder, which contains the client program for the Roman module.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.196.175