NAME
prima-gencls - class interface compiler for Prima core modules
SYNOPSIS
prima-gencls --h --inc --tml -O -I<name> --depend --sayparent filename.cls
DESCRIPTION
Creates C files for Prima core module object definitions.
ARGUMENTS
The prima-gencls program accepts the following arguments:
- --h
-
Generates a .h file ( with the declarations to be included in one or more files )
- --inc
-
Generates a .inc file ( with the declarations to be included in only one file )
- -O
-
Turns on the optimizing algorithm for the .inc files. The algorithm is based on an assumption that some function bodies are identical, and so the duplicates can be detected and removed. When the
-O
flag is set, the body of such a function is replaced with a call to the function with an auto-generated name. That function is not included in the .inc file, but in a .tml file instead. All the duplicate declarations from a set of .tml files can be later removed by the tmlink utility. - --tml
-
Generates a .tml file. Turns the
-O
flag on automatically. - -Idirname
-
Adds a directory to a search path, where the program searches for the .cls files. Can be specified several times.
- --depend
-
Prints out dependencies for the given file.
- --sayparent
-
Prints out the immediate parent of a class inside the given file.
SYNTAX
The syntax of a .cls file can be described by the following scheme:
[ zero or more type declarations ]
[ zero or one class declaration ]
The prima-gencls program produces .h, .inc, and .tml files with the same basename as the .cls file if no object or package name is given, or with the name of the object or the package inside the .cls file otherwise.
Basic scalar data types
Prima-gencls can generate the conversion code for several built-in scalar data types that transfers data between C and perl, using the XS ( see perlguts ) library interface.
These types are:
int
Bool
Handle
double
SV*
HV*
char *
string ( C declaration is char[256] )
There are also some derived built-in types, which are
long
short
char
Color
U8
that are mapped to the int
type. The data undergoes no conversion to int in the transfer process, but it is stored to the perl scalar using the newSViv() function which may lose bits or a sign.
Derived data types
The syntax for a new data type definition is as follows:
<scope> <prefix> <id> <definition>
A scope can be one of two pragmas, global
or local
. That requests the usage locality of the new data type, i e whether the type will be used only for one class or in more than one. Usage of the local
scope somewhat resembles the C predicate static
. The difference between the scopes is that a function using a complex local type in the parameter list, or as a result, will not be optimized out with the -O
flag.
Scalar types
New scalar types may only be aliased to the existing ones, primarily for coding convenience in C. A scalar type can be defined in two ways:
- Direct aliasing
-
Syntax:
<scope> $id => <basic_scalar_type>;
Example:
global $Handle => int;
The new type id will not be visible in the C files, but the type will be substituted over all .cls files that include this definition.
- C macro
-
Syntax:
<scope> id1 id2
Example:
global API_HANDLE UV
Such code creates a C macro definition in the .h header file in the form
#define id1 id2
C macros with parameters are not allowed. id1 and id2 are not required to be present in the .cls namespace, and no substitution during .cls file processing is made.
Complex types
Complex data types can be arrays, structs, and hashes. Prima-gencls allows several combinations of complex data types that C language does not recognize. These will be described below.
The complex data types do not get imported into the perl code. A perl program must conform to the data type used when passing the corresponding parameters to such a function.
- Arrays
-
Syntax:
<scope> @id <basic_scalar_type>[dimension];
Example:
global @FillPattern U8[8];
Example of functions using arrays:
Array * func( Array a1, Array * a2);
Perl code:
@ret = func( @array1, @array2);
Note that the array references are not used, and the number of items in the array parameters must be exactly the same as the dimensions of the arrays.
Warning: the following declaration will not compile with the C compiler, as C cannot return arrays. However, this construct is not treated as an error by prima-gencls:
Array func();
- Structs
-
Syntax:
<scope> @id { <basic_scalar_type> <id>; ... <basic_scalar_type> <id>; };
Example:
global @Struc { int number; string id; }
Example of the functions using structs:
Struc * func1( Struc a1, Struc * a2); Struc func2( Struc a1, Struc * a2);
Perl code:
@ret = func1( @struc1, @struc2); @ret = func2( @struc1, @struc2);
Note that the array references are not used, and both the number and order of items in the array parameters must be exactly as the dimensions and the order of the structs. The struct field names are not used in the perl code as well.
- Hashes
-
Syntax:
<scope> %id { <basic_scalar_type> <id> [with undef]; ... <basic_scalar_type> <id> [with undef]; };
Example:
global %Hash { int number; string id with undef; }
Examples of the functions using hashes:
Hash * func1( Hash a1, Hash * a2); Hash func2( Hash a1, Hash * a2);
Perl code:
%ret = %{func1( \%hash1, \%hash2)}; %ret = %{func2( \%hash1, \%hash2)};
Note that only the hash references are used and returned. When a hash is passed from the perl code it might have some or all fields unset. The C structure is filled and passed to a C function, and the unset fields are assigned to a corresponding C_TYPE_UNDEF value, where TYPE is one of NUMERIC, STRING, and POINTER literals.
If the optional
with undef
declarator was used, the C structure is augmented with additional structundef
with boolean fields that explicitly reflect whether the perl value was passed or not. The C-to-perl conversion respects these boolean flags as well and does not populate hash fields with this bit set.
Namespace section
Syntax:
<namespace> <ID> {
<declaration>
...
<declaration>
}
A .cls file can have zero or one namespace sections filled with function descriptions. Functions described here will be exported to the given ID in the initialization code. A namespace can be either object
or package
.
The package namespace syntax only allows functions without the prefix inside the package
block:
package <Package ID> {
<function description>
...
}
The object namespace syntax allows variables and properties as functions ( called methods in the object syntax ). The general object namespace syntax is
object <Class ID> [(Parent class ID)] {
<variables>
<methods>
<properties>
}
Within the object namespace the inheritance syntax can be also used:
object <Class ID> (<Parent class ID>) { ... }
Functions
Syntax:
[<prefix>] <type> <function_name> (<parameter list>) [ => <alias>];
Examples:
package A {
int func1( int a, int b);
Point func2( Struc * x);
}
object B
method int func1( int a, int b = 1) => c_func_2;
import Point func2( Struc * x, ...);
c_only void func3( HV * profile);
}
The prefix is used with object functions ( methods ) only. More on the prefix in the Methods section.
A function can return nothing ( void ), a scalar ( int, string, etc ), or a complex ( array, hash ) type. It can as well accept scalar and complex parameters, with the type conversion that corresponds to the rules described above in the "Basic scalar data types" section.
If a function has parameters and/or a result of the type that cannot be converted automatically between C and perl, it gets declared but not exposed to the perl namespace. A warning is also issued. It is not possible to use the gencls syntax to declare a function with custom parameters or result types. For such purpose, the explicit C declaration of the code along with a call to newXS
must be made.
Example: ellipsis (...) cannot be converted by prima-gencls, even though it is a legal C construction.
Point package_func2( Struc * x, ...);
The function syntax has several convenience additions:
- Default parameter values
-
Example:
void func( int a = 15);
A function declared in such a way can be called both with 0 or 1 parameters. If it is called with 0 parameters, an integer value of 15 will be automatically used. The syntax allows default parameters for types int, pointer, and string, and their scalar aliases.
The default parameters can be as many as possible, but they have to be at the end of the function parameter list. The declaration
func( int a = 1, int b)
is incorrect. - Aliasing
-
In the generated C code, a C function has to be called after the parameters have been parsed. Prima-gencls expects a conforming function to be present in the C code, with the fixed name and parameter list. However, if the only task of such a function is to be a one-to-one wrapper to the identical function published under another name, aliasing can be performed to save both code and speed:
Example:
package Package { void func( int x) => internal; }
A function declared in that way will not call the Package_func() C function, but the internal() function instead. The only request here is that the internal() function must have the same C parameters and result as the func() function.
- Inline hash
-
If a function is declared with the last parameter of the
HV*
type then the parameter translation from perl to C is performed as if all the parameters passed are a hash. This hash is passed to the C function and its content is returned then back to perl as a hash again. The hash content can be modified inside the C function.This declaration is used heavily in constructors, that are coded in perl typically like this:
sub init { my %ret = shift-> SUPER::init( @_); ... return %ret; }
and the corresponding C code is
void Obj_init ( HV * profile) { inherited init( profile); ... [ modify profile content ] ... }
Methods
Methods are the functions called in the context of an object. Virtually all class methods need to have access to the object they are bound to. Prima objects are visible in C as the Handle
data type. The Handle is a pointer to the object instance which in turn contains a pointer to the object virtual methods table ( VMT ). To facilitate the OO-like syntax, this Handle parameter is rarely mentioned in the method declarations:
method void a( int x)
however, the signature of the corresponding C function contains the Handle parameter
void Object_a( Handle self, int x)
Methods are accessible in C code by the direct name dereferencing of the Handle self
like this:
((( PMyObject) self)-> self)-> my_method( self, ...);
A method can have one of the following six prefixes that produce different C code wrappers:
- method
-
This is the most basic method type. Methods of this type are expected to be coded in C, the object handle is implicit and is not included in the .cls function declaration:
method void a()
results in
void Object_a( Handle self)
C declaration. The published method automatically converts its parameters and the result between C and perl.
- public
-
When the methods need to have parameters and/or a result that cannot be automatically converted between C and perl, or the function declaration does not fit into the C syntax, the
public
prefix is used. The methods declared aspublic
are expected to communicate with perl by through the XS ( see perlxs) interface. It is also expected that the programmer declares the REDEFINED and FROMPERL functions in C code( see Prima::internals for details). Examples are many throughout the Prima source and will not be shown here.public
methods usually have the void result and no parameters, but that does not matter since prima-gencls provides no data conversion for such methods anyway. - import
-
For the methods that are best implemented in perl instead of C, prima-gencls can produce the C-to-perl wrappers using the
import
prefix. Animport
function does not need a C counterpart, only the auto-generated code. - static
-
If the method has to be able to work both with and without an object instance, it needs to be prepended with the
static
prefix.static
methods are similar tomethod
except that theHandle self
first parameter is not implicitly declared. If thestatic
method is called without an object ( but with a class ), like for exampleClass::Object-> static_method();
its first parameter is not an object but the "Class::Object" string. If the method never uses that first parameter it is enough to declare it as
static a( char * className = "");
but if it does, a
static a( SV * class_or_object = NULL);
declaration is needed. In the latter case C code itself has to determine what exactly has been passed, if ever. Note the default parameter here: a
static
method is usually legible to call asClass::Object::static_method();
where no parameters are passed to it. Without the default parameter, such a call generates an 'insufficient parameters passed' runtime error.
- weird
-
We couldn't find a better name for it. The
weird
prefix describes a method that combines properties from both thestatic
andpublic
methods. Prima-gencls generates no conversion code for theweird
methods and expects noHandle self
as the first parameter. As an example, the Prima::Image::load function can be called using a wide spectrum of calling semantics ( see Prima::image-load for details). - c_only
-
The
c_only
methods are present in the VMT but are not accessible from perl. They can be overloaded, but from C only. Moreover, it is allowed to register a perl function with the same name as the existingc_only
method, and these entities will be completely independent.Note: methods that have a result and/or parameters declared as data types that cannot be converted automatically, change their prefix to
c_only
during the .cls processing. Probably this is the wrong behavior, and such a condition has to signal an error.
Properties
The Prima toolkit introduces an entity named property, that is used to replace method pairs whose function is to acquire and assign some internal object variable, for example, an object name, color, etc. Instead of having a pair of methods like Object::set_color and Object::get_color, a property Object::color can be used instead. A property is a method with special considerations, in particular, when it is called without parameters, the get-mode is implied. On the contrary, if it is called with one parameter, such a call is treated as being done in the set-mode. Note that in both 'set' and 'get' invocations the first parameter Handle self
is implicit and is always present.
Properties can operate with different, but always fixed number of parameters. For example,
property char * name
has the C counterpart
char * Object_name( Handle self, Bool set, char * name)
Depending on the calling mode, the Bool set
argument is either true
or false
. In the set-mode, the C code result is discarded, while in the get-mode the property parameter value is undefined.
The syntax for a multi-parameter property is
property long pixel( int x, int y);
with the C code
long Object_pixel( Handle self, Bool set, int x, int y, long pixel)
Note that in the multi-parameter case, the parameters declared after the property name are always initialized, in both the set- and get- modes.
Instance variables
The prima-gencls syntax allows variable declarations for the variables that are allocated for every object instance. Although data type validation is not performed for variables, and their declarations just get copied as is, complex C declarations involving array, struct, and function pointers are not supported. As a workaround, pointers to typedef'd entities are used. Example:
object SampleObject {
int x;
List list;
struct { int x } s; # illegal declaration
}
The variables are accessible in C code by direct name dereferencing the Handle self
:
(( PMyObject) self)-> x;
AUTHORS
Dmitry Karasik, <dmitry@karasik.eu.org>. Anton Berezin, <tobez@tobez.org>.
SEE ALSO
COPYRIGHT
This program is distributed under the BSD License.