David Mertens > C-TinyCompiler-0.04 > C::TinyCompiler::Callable

Download:
C-TinyCompiler-0.04.tar.gz

Dependencies

Annotate this POD

View/Report Bugs
Module Version: 0.04   Source  

NAME ^

C::TinyCompiler::Callable - A C::TinyCompiler-based foreign function interface, i.e. C-functions callable from Perl

SYNOPSIS ^

 use strict;
 use warnings;
 use C::TinyCompiler;
 
 # Make a few functions we can invoke from Perl:
 my $context = C::TinyCompiler->new('::Callable');
 $context->code('Body') = q{
     
     /* All Perl-callable functions should have
      * this string placed immediately before the
      * function declaration or definition: */
     C::TinyCompiler::Callable
     double positive_pow (double value, int exponent) {
         double to_return = 1;
         while (exponent --> 0) to_return *= value;
         return to_return;
     }
     
     C::TinyCompiler::Callable
     double my_sum (double * list, int length) {
         int i;
         double to_return = 0;
         for (i = 0; i < length; i++) {
             to_return += list[i];
         }
         return to_return;
     }
 };
 $context->compile;
 
 # Retrieve the function references:
 my $pow_subref = $context->get_callable_subref('positive_pow');
 my $sum_subref = $context->get_callable_subref('my_sum');
 
 # Exercise the pow subref
 print "3.5 ** 4 is ", $pow_subref->(3.5, 4), "\n";
 
 # Exercise the list summation:
 my @values = map { rand() } (1..10);
 my $doubles_buffer = pack('d*', @values);
 print join(' + ', @values), " = ",
     $sum_subref->(\$doubles_buffer, scalar(@values));

DESCRIPTION ^

This module implements the machinery necessary to easily call C functions from Perl with arbitrarily many arguments. The types of the arguments are a bit restricted at the moment, but it nonetheless works for a fairly broad variety of things. In particular, it handles pointers just fine.

Like all other C::TinyCompiler packages, you make this module available as a package to your compiler state either by indicating it as an argument to your state's constructor or by applying the package later:

 # Create compiler state with C::TinyCompiler::Callable
 my $compiler = C::TinyCompiler->new('C::TinyCompiler::Callable');
 # Slighly abbreviated:
 my $compiler = C::TinyCompiler->new('::Callable');
 
 # Adding it after construction:
 my $compiler = C::TinyCompiler->new;
 $compiler->apply_packages('C::TinyCompiler::Callable');

The primary operation of this package is to scan through your code, identify functions for which you want to build Perl functions, and build the necessary C and Perl code. You indicate which of your functions should have Perl interfaces by preceding them "immediately" with the package name:

 $compiler->code('Body') .= q{
     /* This will have a Perl interface */
     C::TinyCompiler::Callable
     void do_something(int arg1, double arg2) {
         ...
     }
     
     /* This will not have a Perl interface */
     void something_else (double foo, int bar) {
         ...
     }
     
     C::TinyCompiler::Callable  /* Also has a Perl interface */
     void third_func(int arg1, double arg2) {
         ...
     }
     
     /* comments among arguments is even ok */
     C::TinyCompiler::Callable
     void get_height_and_temp (
         /* Input: grid, location on the grid, and time of day */
         grid * my_grid, int x, int y, int time,
         /* Output: height and temperature of that position at that time */
         double * height, double * temperature
     ) {
         ...
     }

 };

The extraction parser does not care about whitespace, and is smart enough to consider classic C-style comments (i.e. /* comment */ but not // comment) as whitespace. This is true even within the function's arguments (i.e. get_height_and_temp) and between the Callable declaration and the function declaration (i.e. third_func).

Just before your code is run through the preprocessor, each function marked as callable will be examined and a new C-level invocation function with a standard argument layout will be added to the Foot section. Also, the text for a Perl function that knows the name of the C-level invocation function and knows how to invoke it will be generated.

After you have compiled your code, you can obtain a Perl subref that invokes your code by calling the get_callable_subref method, supplying the function's name. You can then invoke this method with your Perl arguments.

Basic Usage

The basic usage was already demonstrated in the Synopsis. If you use basic C types, you should be able to just put C::TinyCompiler::Callable before the function declaration and you will be able to invoke your code from Perl in the obvious way.

Working with pointers and C-level arrays

There are many ways that your Perl code might refer to arbitrary C data. One is to have a Perl scalar whose integer value is the pointer address. Another is to have the actual binary data stored in a Perl's PV slot, which is ordinarily reserved for the string portion of the scalar. Since there is no clean way to ascertain your scalar's provenance, C::TinyCompiler::Callable introduces the following convention: if your scalar is a reference, it assumes that the PV slot of the referent is a binary blob, and uses the address of that PV slot as the argument for a pointer. If your scalar is not a reference, it assumes that the IV slot of your scalar is a pointer address.

How does this work in practice? If you need to pass a string to your C code, pass a reference to the actual variable holding the data instead of passing the variable itself:

 my $is_legit = $confirm_id->(\$user_id, \$user_password);
 #                            ^          ^
 # note reference             |          |

Similarly, if you pack a collection of data, you pass a reference to that packed collection:

 my $packed_data = pack 'd*', @values;
 my $sum = $sum_data->(\$packed_data, scalar(@values));
 #                     ^
 # note referece       |

If you are an XS module writer and you want to allow somebody to get a hold of a pointer to some data so they can manipulate it, you simply use PTR2IV:

 /* in XS */
 IV
 get_pointer_for_array(self)
     SV * self
     CODE:
         ...
         RETVAL = PTR2IV(data_array);;
     OUTPUT:
         RETVAL

The resulting scalar will be perfect for sending to a function that expects a pointer:

 my $data_ptr = $obj->get_pointer_for_array;
 my $length = $obj->length;
 my $sum = $sum_data->($data_ptr, $length);
 #                     ^
 # No reference!       |

Working with Perl-level objects

If you have Perl objects with data that you want to pass to a Callable function, you can add the pack_as method to your class to make life easier. When your function gets an object, it will ask it if it can pack as the needed type, such as int or double *. This way your object can present meaningful data when used as an argument.

Assuming the ficticious object mentioned in the previous section holds an array of doubles, one could implement a pack_as method for tha class like so:

 sub pack_as {
     my ($self, $type) = @_;
     return $self->length if $type eq 'int';
     return $self->get_pointer_for_array
         if $type =~ /^double\s*[*]$/;
     croak('Foo can only be cast by C::TinyCompiler::Callable as a double* or int');
 }

Then the user could invoke the my_sum function from the synopsis like so:

 my $sum = $my_sum_subref->($obj, $obj);

Caveats about Speed

This is an interface to C::TinyCompiler, and the resulting code compiles down to C code. However, the code generated this way still has to jump through a lot of hoops.

I have tried to optimize the generated code as much as possible, but the path from your Perl invocation of the generated subref to your actual C code involves: (1) invoking the subref with your arguments, which goes on to (2) unpack those arguments from @_, (3) ask those arguments if they are objects and know how to cast themselves to the required C type, and if not use a default casting mechanism, (4) pack all of the arguments into a single C binary buffer, (5) build a return binary buffer, (6) call an XS "trampoline" that calls the C wrapper with the input and output binary buffers, which (7) unpacks the binary input buffer into C variables, (8) invokes the original function with the unpacked C variables, (9) packs the return value into the supplied binary return buffer and returns (it's actually a void function), bringing us back to the subref which (10) unpacks the binary return buffer and returns the value.

Contrast those steps with the steps needed for calling a simple xsub wrapper. The steps involve (1) invoking the xsub with your arguments in Perl which (2) unpacks the stack in your XS/C code, (3) invokes the original function with the unpacked C variables, and (4) packs the return value(s) into SVs and places them on the return stack.

In short, don't use this sort of functionality to add two integers. If you need to perform complicated time stepping on a mesh, though, the extra overhead of the function invocation will be relatively innocuous.

AUTHOR ^

David Mertens, <dcmertens.perl at gmail.com>

BUGS ^

Please report any bugs or feature requests at the project's main github page: http://github.com/run4flat/perl-TCC/issues.

SUPPORT ^

You can find documentation for this module with the perldoc command.

    perldoc C::TinyCompiler::Callable

You can also look for information at:

SEE ALSO ^

There are a number of modules or tools that do something similar to what C::TinyCompiler::Callable does. All of these modules use another compiler to generate the object code in use, so they will present tradeoffs in compile time and execution time compared with C::TinyCompiler::Callable.

C::TinyCompiler::Perl pulls in all of the Perl headers, which in principle would allow you to write your own Perl-callable C functions that manipulate the stack and everything. Of course, that is not documented and it's likely to be quite verbose, so it's not for the faint of heart. But in principle, that mechanism provides another way to generate hooks so you can call C functions from Perl.

For a runtime foreign function interface, see FFI::Raw, which mostly supersedes FFI (though the latter might be worth looking into). This module uses assembly to build a C stack and then directly fire your C function. Building the stack takes a little more effort on your part, but those details can easily be encapsulated in a Perl-side wrapper. The only reason I didn't use that module as this module's C function caller is that it is not (yet) as general as I would like and I didn't want to introduce another nontrivial dependency.

A more powerful means for building C functions and calling them from Perl is Inline::C. This has the full power of XS, and the full optimization of your local compiler, but is not designed to quickly compile. Generally, I find that Inline::C is a great means for prototyping my XS code, but the assumption is that eventually the codebase will reach a stable state. If you need to dynamically generate lots of different C functions throughout your code's execution, the compiler invocation overhead of Inline::C will likely not pay off compared to the speed of tcc's compilation.

If your goal is to write a stable set of C functions that can be called from Perl, you should ultimately look into Perl's C interface layer, perlxs. XS is generally meant to be used in the context of a module or suite of modules, and it is nicely wrapped by Inline::C (and more basically by Inline::XS). If you do not need to generate C code on the fly, this is the best means for writing code that quickly invokes and executes. The Perl hooks into your code will be the most direct of all of these options, and the generated object code will be optimized by your compiler in ways that tcc likely will not be.

LICENSE AND COPYRIGHT ^

Portions of this module's code are copyright (c) 2013 Northwestern University.

Portions of this module's code are copyright (c) 2013 Dickinson College.

This module's documentation is copyright (c) 2013 David Mertens.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

syntax highlighting: