C::TinyCompiler::Callable - A C::TinyCompiler-based foreign function interface, i.e. C-functions callable from Perl
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));
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).
/* comment */
// comment
get_height_and_temp
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.
Foot
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.
get_callable_subref
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.
C::TinyCompiler::Callable
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:
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! |
It may be that you want to write a function that directly manipulates an SV*. For such a task, using XS::TCC is probably the better TCC-related tool for this job. You can use C::TinyCompiler, though. First, you will need to include C::TinyCompiler::Perl in your compiler context. Then, your C function will have an SV* in the argument list:
SV*
C::TinyCompiler::Perl
$context = C::TinyCompiler->new('::Perl', '::Callable'); $context->code('Body') = q{ C::TinyCompiler::Callable void my_set_IV (SV * a, int b) { sv_setiv(a, b); } }; $context->compile;
Finally, when calling the function, you must pass a reference to the scalar that you want sent to the function:
my $my_set_IV = $context->get_callable_subref('my_set_IV'); my $to_set; # Set variable to 10 $my_set_IV->(\$to_set, 10);
I know, this is annoying, but I couldn't find a way to get the original SV to the C function. And if we can't get the original SV to the C function, then the C function wouldn't be able to make any modifications to the SV. And what's the fun in that?
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.
pack_as
int
double *
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 = $my_sum_subref->($obj, $obj);
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.
@_
pack
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.
David Mertens, <dcmertens.perl at gmail.com>
<dcmertens.perl at gmail.com>
Please report any bugs or feature requests at the project's main github page: http://github.com/run4flat/perl-TCC/issues.
You can find documentation for this module with the perldoc command.
perldoc C::TinyCompiler::Callable
You can also look for information at:
The Github issue tracker (report bugs here)
http://github.com/run4flat/perl-TCC/issues
AnnoCPAN: Annotated CPAN documentation
http://annocpan.org/dist/C-TinyCompiler
CPAN Ratings
http://cpanratings.perl.org/d/C-TinyCompiler
Search CPAN
http://p3rl.org/C::TinyCompiler http://search.cpan.org/dist/C-TinyCompiler/
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.
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.
To install C::TinyCompiler, copy and paste the appropriate command in to your terminal.
cpanm
cpanm C::TinyCompiler
CPAN shell
perl -MCPAN -e shell install C::TinyCompiler
For more information on module installation, please visit the detailed CPAN module installation guide.