Perl Embedding

An overview of what is needed to make your favorite application Perl-enabled and how to avoid some obstacles along the way.

This article describes my experience embedding Perl into an existing application. The application chosen, sc, is a public-domain, character-based spreadsheet that often comes as part of a Linux distribution. The reason for choosing sc was twofold. First, I use sc for any spreadsheet-type tasks I have. Second, I was somewhat familiar with the source, because I once added code in order to format dates the way I wanted. Besides, I always thought it would be nice if sc had some sort of macro language—everything should be programmable in some way.

Getting Started

The first thing I did was to get the sc 6.21 source and compile it on my machine. This ensured that everything worked from the start, before I started making modifications to the code.

The next thing was to add the necessary code to sc.c to embed the Perl interpreter. The basics of this were:

  • Add the following include files

            #include <EXTERN.h>
            #include <perl.h>
    

  • Add the variable for the Perl interpreter

            static PerlInterpreter *MyPerl;
    

  • Put the code shown in Listing 1 in main in the file sc.c.

  • Update Makefiles to use the correct parameters. This consisted of adding CC options and linker options derived from the following commands:

            perl -MExtUtils::Embed -e ccopts
            perl -MExtUtils::Embed -e ldopts
    
    These commands give you the compiler and linker options that your version of Perl was compiled with.

Nothing else needs to be done; the Perl interpreter is now in the code. Obviously you can't do anything yet, but you can work out any compilation problems. Right away, I had a few problems with some #define statements and a prototype for main. EXT and IF were the two offending #defines. I fixed these by appending “sc” to the end of them wherever they occurred in the original sc code, to make them unique. If you were writing an application from scratch, it would not be a bad idea to prepend a common prefix to each #define.

Perl, on the other hand, expected main to have a third argument, env, so I added it. I am still not sure where this argument comes from, but it doesn't seem to create any problems.

Running Perl Code

Once the base interpreter compiled successfully, I needed a way to call the functions. I looked at the sc source and found that one of the keystrokes, ctrl-k, was free for my use. I used this as my “call Perl” key-command macro, with macros from 0 to 9 defined. This combination calls predefined Perl subroutines called sc_macro_[0-9], when defined. The code in Listing 2 adds this functionality.

The function call_sc_perl_macro checks first to see if the subroutine exists with perl_get_cv. If null is not returned, it calls the function which has the name sc_macro_# where # is a digit from 0 to 9.

The perl_call_va function comes from Sriram Srinivasan's book Advanced Perl Programming, published by O'Reilly. This code was used to expedite my ultimate goal of embedding Perl into sc. The code for perl_call_va can be found in the file ezembed.c.

With sc compiled, I proceeded to test the interpreter by creating dummy macros in the file sc.pl to write some data to temporary files. Everything worked fine, which told me the Perl interpreter was working inside of sc.

Automating sc

With a working Perl interpreter embedded into sc and the ability to call Perl “macros”, the interfaces to the C functions in sc needed to be created to do useful work. Fortunately, sc is laid out nicely enough that, for the most part, all one has to do is wrap an already existing function and interface with its internal command parser.

The first thing I thought might be useful is to move the current cell around. Without that ability, I would be able to operate only on a single cell, which is not very useful. Besides, it was one of the least complicated sections of code and provided a good start.

The code for sc_forward_row is shown in Listing 3 and found in sc_perl.c. Before I describe this code, let me give you a quick overview of how Perl treats scalars. Each scalar has many pieces of information, including a double numeric value, a string value and flags to indicate which parts are valid. For our purposes, the scalar can hold three types of values: an integer value (IV), a double value (NV) and a string value (PV). For any scalar value (SV), you can get to their respective values with the macros SvIV, SvNV and SvPV.

Now, in the Listing 3 code, XS is a Perl macro that defines the function. dXSARGS sets up some stuff for the rest of XSub, such as the variable items that contains the number of items passed to Xsub on the Perl argument stack. If the argument count does not equal 1, XS_RETURN_IV returns 1 to Perl to indicate an error. Otherwise, the top element of the Perl argument stack, ST(0), is converted to an integer value and passed to the forwrow function.

Note that all of the XSub code was generated by hand. Some of this work can be done with Perl's xsubpp or with a tool called swig, but in this case, I felt it was simpler to code it myself.

Finally, tell the Perl interpreter about this Xsub with the statement:

newXS("sc_forward_row",sc_forward_row,"sc_perl.c");

The first argument is the name of the subroutine in Perl. The next argument is the actual C routine (in this case they are the same, but they don't have to be). The last argument is the file in which the subroutine is defined, and is used for error messages. I chose to create all of the newXS functions by parsing my sc_perl.c file with a Perl script, so that I would not have to do two things every time I added a new XSub.

______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState