The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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 struct undef 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 as public 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. An import 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 to method except that the Handle self first parameter is not implicitly declared. If the static method is called without an object ( but with a class ), like for example

   Class::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 as

  Class::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 the static and public methods. Prima-gencls generates no conversion code for the weird methods and expects no Handle 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 existing c_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

Prima::internals, tmlink

COPYRIGHT

This program is distributed under the BSD License.