Inline::C Techniques

Chapter 10 - Writing C Modules with Inline::C
by?Sam Tregar?
Apress ? 2002
has companion web siteCompanion Web Site

This section will explore ways that Inline::C can be used to enhance the basic inlined function shown earlier. Many of these techniques will be very similar to those shown in the "XS Techniques" section in Chapter 9. This is natural; Inline::C is just a layer on top of XS, so many of the things that can be done with XS can be done the same way in Inline::C.

Using Typemaps

Typemaps work mostly the same way in Inline::C as they do in XS. The biggest difference is that to use a typemap with Inline::C, you have to include the TYPEMAPS option:

 use Inline C          => 'DATA',            NAME       => "Gnome::MIME",            VERSION    => "0.01",            LIBS       => 'gnome-config gnome --libs',            INC        => 'gnome-config gnome --cflags',            TYPEMAPS   => 'typemap'; 

The TYPEMAPS option is set to the path of the typemap file. For example, to use the gchar type in the file_type(), I would create a file called typemap and put a single line in it:

 gchar * T_PV 

Now the file_type() function can written as follows:

 char * file_type (gchar * filename) {   return gnome_mime_type(filename); } 

Inline::C uses typemap files in two ways. They're used by the generated XS to bind function parameters and return values in the same way as in the XS section. However, they're also used by the Inline::C parser to determine which functions can be wrapped for use by Perl. Inline::C will silently ignore functions with signatures that don't have matching typemaps. The result is that if Inline::C doesn't accept your typemap for one reason or another, it will simply ignore functions that are trying to use that typemap. You can find out if this is happening by setting the PRINT_INFO option:

 use Inline C          => 'DATA',            NAME       => "Gnome::MIME",            VERSION    => "0.01",            LIBS       => 'gnome-config gnome --libs',            INC        => 'gnome-config gnome --cflags',            TYPEMAPS   => 'typemap',            PRINT_INFO => 1; 

This will cause Inline::C to produce the same information block shown earlier with the –MInline=INFO invocation. Included is a list of functions bound by Inline::C; if functions are missing, then you may have a problem with your typemaps.

Supporting Named Parameters

One way in which XS modules and Inline::C modules are essentially the same is that they both benefit from careful interface design. In terms of Gnome::MIME, this means supporting a named-parameter style interface to file_type():

 $type = Gnome::MIME::file_type(filename     => $filename,                                default_type => "text/html",                                read_file    => 1); 

The full inlined function implementing this interface is shown in Listing 10-3.

Listing 10-3: Inline Function with Named-Parameter Support

start example
 char * file_type (SV *dummy, ...) {   Inline_Stack_Vars;         // get access to the Inline stack macros   char *filename = NULL;     // variables for named params values   char *default_type = NULL;   IV read_file = 0;   int x;                     // loop counter   // loop over args by pairs and fill in parameters   for (x = 0; x < Inline_Stack_Items; x+=2) {     char *key = SvPV(Inline_Stack_Item(x), PL_na);     if (strEQ(key, "filename")) {       filename = SvPV(Inline_Stack_Item(x+1), PL_na);     } else if (strEQ(key, "default_type")) {       default_type = SvPV(Inline_Stack_Item(x+1), PL_na);     } else if (strEQ(key, "read_file")) {       read_file = SvIV(Inline_Stack_Item(x+1));     } else {       croak("Unknown key found in Gnome::MIME::file_type parameter list: %s",             key);     }   }   // make sure we have a filename parameter   if (filename == NULL) croak("Missing required parameter filename.");   // call the appropriate function based on arguments   if (read_file && default_type != NULL)     return gnome_mime_type_or_default_of_file(filename, default_type);   if (read_file)     return gnome_mime_type_of_file(filename);   if (default_type != NULL)     return gnome_mime_type_or_default(filename, default_type);   return gnome_mime_type(filename); } 
end example

This function is very similar to the XS implementation introduced earlier, but there are some significant differences. First, the function's signature is different:

 char * file_type (SV *dummy, ...) { 

The dummy argument is required by Inline::C; due to the way it compiles C code to XS, it won't allow an argument list of ( ... )like the XS implementation uses. This makes it impossible to create an Inline::C function that can take zero or more parameters. I expect this limitation to be removed in a future version of Inline::C.

Next, a special Inline::C macro is used to initialize some temporary variables employed by the other Inline::C stack macros:

    Inline_Stack_Vars;       // get access to the Inline stack macros 

These macros are included in the block of code following the variable declarations:

   // loop over args by pairs and fill in parameters   for (x = 0; x < Inline_Stack_Items; x+=2) {     char *key = SvPV(Inline_Stack_Item(x), PL_na);     if (strEQ(key, "filename")) {       filename = SvPV(Inline_Stack_Item(x+1), PL_na);     } else if (strEQ(key, "default_type")) { 

This block of code uses Inline_Stack_Items where the XS code used items, and Inline_Stack_Item where the XS code used ST. Their meaning is the same though, and they can be used interchangeably in Inline::C code.

The final change is that with the absence of the RETVAL special variable the control flow in the final section is simplified. The function simply returns when it has found the correct function to call:

   // call the appropriate function based on arguments   if (read_file && default_type != NULL)     return gnome_mime_type_or_default_of_file(filename, default_type); 

Returning Multiple Values Using Inline::C

Rounding out the set of XSUBs redone as inlined functions, Listing 10-4 contains an Inline::C version of the type_data() function from Chapter 9.

Listing 10-4: Inline Function Using Multivalue Return

start example
 void type_data(gchar *type) {   Inline_Stack_Vars;   GList *keys, *iter;   HV *hv;   SV *value;   char *key;   // initialize hash   hv = newHV();   // get GList of keys for this type   keys = gnome_mime_get_keys(type);   // iterate through keys   for (iter = keys; iter; iter = iter->next) {     // get the key from the iterator     key = iter->data;     // create a new SV and load it with the value for this key     value = newSVpv(gnome_mime_get_value(type, key), 0);     // store the key/value pair in     hv_store(hv, key, strlen(key), value, 0);   }   // free keys GList   g_list_free(keys);   // test context with GIMME_V   if (GIMME_V == G_ARRAY) {     // list context - return a list of key/value pairs     int count = hv_iterinit(hv);     int i;     I32 len;     // get ready for Inline_Stack_Push     Inline_Stack_Reset;     // loop over key/value pairs     for (i = 1; i <= count; i++) {       value = hv_iternextsv(hv, &key, &len);       // push key and value       Inline_Stack_Push(sv_2mortal(newSVpvn(key, len)));       Inline_Stack_Push(sv_mortalcopy(value));     }     // done pusing on the stack     Inline_Stack_Done;     // free hv explicitly     SvREFCNT_dec((SV *)hv);     // return two SVs for each key in the hash     Inline_Stack_Return(count * 2);   }   // G_SCALAR or G_VOID context return a reference to the new hash   Inline_Stack_Reset;   Inline_Stack_Push(sv_2mortal(newRV_noinc((SV *)hv)));   Inline_Stack_Done;   Inline_Stack_Return(1); } 
end example

This function returns a hash reference in scalar context and a list of key-value pairs in list context:

 # scalar context $type_data = Gnome::MIME::type_data("text/html"); $program = $type_data->{open}; # list context %type_data = Gnome::MIME::type_data("text/html"); $program = $type_data{open}; 

The code used is substantially similar to the XS version, and again the difference is largely a matter of the Inline::C macros used.

To start, the function begins with a void return type, which works similarly to a PPCODE block in XS in that it allows you to handle the return stack explicitly:

 void type_data(gchar *type) { 

Next, the Inline_Stack_Vars macro is used to initialize temporaries for the Inline::C stack macros:

 Inline_Stack_Vars; 

The next new macro usage is the pair Inline_Stack_Reset and Inline_Stack_Done. These are required around any usage of Inline_Stack_Push. These macros push the list of key/value pairs onto the return stack:

   // get ready for Inline_Stack_Push  Inline_Stack_Reset;   // loop over key/value pairs   for (i = 1; i <= count; i++) {     value = hv_iternextsv(hv, &key, &len);     // push key and value     Inline_Stack_Push(sv_2mortal(newSVpvn(key, len)));     Inline_Stack_Push(sv_mortalcopy(value));   }  // done pushing on the stack  Inline_Stack_Done; 

Then Inline::C's version of XSRETURN, Inline_Stack_Return, is used to return from the function:

     // return two SVs for each key in the hash     Inline_Stack_Return(count * 2); 

A similar sequence is used in scalar context to return a single value:

   // G_SCALAR or G_VOID context return a reference to the new hash   Inline_Stack_Reset;   Inline_Stack_Push(sv_2mortal(newRV_noinc((SV *)hv)));   Inline_Stack_Done;   Inline_Stack_Return(1); 

This marks a difference from the XS code where the final XSRETURN(1) wasn't required; in Inline::C Inline_Stack_Return is a required call regardless of the number of return values.



Writing Perl Modules for CPAN
Writing Perl Modules for CPAN
ISBN: 159059018X
EAN: 2147483647
Year: 2002
Pages: 110
Authors: Sam Tregar

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net