Traditionally, the main generator file is called
gen_XYZ.pl
and lies in a directory XYZ
where the
code generation for TAG
is going to take place. We
start by giving an example that is available in the MPFQ
distribution in src/gf7/gen_gf7.pl
:
#!/usr/bin/perl -w use warnings; use strict; use File::Spec; my $dirname; BEGIN { $dirname = (File::Spec->splitpath($0))[1]; unshift @INC, "$dirname/../perl-lib"; } use Mpfq::engine::conf qw(read_api); use gf7; # Use the default api file. my $api_file = "$dirname/../api.pl"; MAIN: { # Read api file. At that point, one can also pass one or several # api_extensions monikers. my $api = Mpfq::engine::conf::read_api $api_file, qw/POLY URE/; my $options = {}; my $code = {}; # This calls code_for_xxx for all xxx functions in the api. my $object = gf7->new(); $object->create_code($api, $code, $options); # This creates the .c and .h files. $object->create_files('.', 'gf7'); }
The code generation proceeds in 4 steps:
Reading the API. This is done using the
read_api()
function, which takes a file, and
optionally, realms. It returns a hash that is usually stored
in a variable $api
that will be passed as an
argument to other functions of the code generation.
Creating the object of the proper type. For the moment this involves only calling the new
method of the chosen package. The methods below for code generations are methods of that object, since all objects considered inherit from common MPFQ bare bones.
Creating the code with create_code()
. This is
the function that calls all the code_for_xxx
functions that creates C code. It works by side-effect on the
$code
variable. Prior to calling the code
generating functions, create_code()
calls the
init_handler()
if present in the packages within the ancestry of the class of the object.
Upon exit it
similarily calls the exit_handler()
function.
Each init_handler()
should return
hash entries to be inserted in the $code
parameter. It is also possible to modify the $code
hash directly from the main generator, instead of from a package file (although best practices favor the latter).
Creating the files with create_files()
. This
write the resulting .c
and .h
files. The first argument specifies a path for the output
files, which are named mpfq_TAG.c
and
mpfq_TAG.h
, where TAG
denotes the
second argument.
The main generator file is rather short, and does very little beyond the steps above. We go through its different sections in order.
#!/usr/bin/perl -w use warnings; use strict;
It is handy to make the main generator file an executable perl
script.
use File::Spec; my $dirname; BEGIN { $dirname = (File::Spec->splitpath($0))[1]; unshift @INC, "$dirname/../perl-lib"; }
The fragment above is the chosen mechanism to setup perl
's include path to also contain the perl-lib
subdirectory at the upper level.
use gf7;
This loads the top-level package file (described in the previous section).
use Mpfq::engine::conf qw(read_api); my $api_file = "$dirname/../api.pl"; my $api = Mpfq::engine::conf::read_api $api_file, qw/FIELD POLY/;
This is mandatory as one of the tasks of the main generator file is to read the MPFQ API file.
my $object = gf7->new();
This creates an object of the class defined by the top-level package file.
my $code = {}; my $options = {};
The two (references to) hashes above must be created. The first array will hold the complete generated codde, while the $options
hash is passed as an argument to all code generation functions. It is a convenient way to pass information to these functions.
$object->create_code($api, $code, $options);
This effectively does the code generation in memory (within the $code
hash, to which $object
takes a reference).
$object->create_files('.', 'gf7');
and this final statement creates the source files.
The definition of the types required by the API can be done at
various places, including in the MAIN, like in the example. These
types have to be added as the types
member of the
$code
hash.
All along the generation, one might want to pass some information
to the code_for_xxx
; the $options
is meant
for that. What to put in this hash is up to the implementor.
Let us summarize the meaning of the 3 important variables:
$code
is a hash. Here is the expected contents
at the end of the process (before entering
create_files()
).
There should be: an entry for each function to implement:
$code->{'add'} = ...;
where ... is a hash reference describing the implementation.
The implementation may be tagged as macro, function, or inlined.
See perl-lib/Mpfq/handler.pm
(comments before
the ship_code
function) for the documentation of
the other hash members.
There must be an entry describing types (generated either
from
the gen_TAG.pl
or from some
init_handler()
as described above)
$code->{'types'} = { elt => "typedef unsigned long $elt\[17\];", dst => "typedef unsigned long * $dst;", src => "typedef const unsigned long * $src;", ... }
It is possible to have a cpp_asserts
entry. This
will translates into some assertion at the preprocessor
level. For instance, use :
$code->{'cpp_asserts'} = [ "GMP_LIMB_BITS == 64" ];
if the generated code is only for 64 bit computers.
Another important field is for #include
that are
required by the implementation. The includes that must be
added at the top of the .h
file are to be
declared as follows:
$code->{'includes'} = [ '"my_macros.h"', '"grouik.h"' ];
More complicated statements that must be put at the top
of the .h
can be declared in the
extra
field:
$code->{'extra'} = [ "#ifndef __GNU_C_\n#warning \"non free compiler!\"\n#endif" ];
Finally, $code->{'banner'}
is a banner to put
in the head of .c
and .h
files.
$options
is a hash. It is passed as an argument
to every creator. This is up to you to decide which option is
relevant for your implementation.
$api
is another hash. This is essentially a copy
of the api.pl
file and there is nothing to play
with.