The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Code::Class::C - Perl extension for creating ANSI C code from a set of class definitions to accomplish an object-oriented programming style.

SYNOPSIS

  use Code::Class::C;
  my $gen = Code::Class::C->new();
  
  $gen->class('Shape',
    subs => {
      'getLargest(s:Shape):Shape' => 'c/Shape.getLargest.c',
      'calcArea():float' => q{
        return 0.0;
      },
    },
  );
  
  $gen->class('Circle',
    isa => ['Shape'],
    attr => {
      'radius' => 'float',
    },
    subs => {
      'calcArea():float' => q{
        return 3.1415 * self->radius * self->radius;
      },
    },
  );
  

DESCRIPTION

This module lets you define a set of classes (consisting of attributes and methods) and then convert these definitions to ANSI C code.

The "cool" thing is, that the methods are written in C and you can use the classes in an object-oriented fashion.

Constructor

new()

  my $gen = Code::Class::C->new();
  

The constructor of Code::Class::C takes no arguments and returns a new generator instance with the following methods.

Methods

class()

The class() method lets you define a new class:

  $gen->class('Circle',
    isa => ['Shape'],
    attr => {
      'radius' => 'float',
    },
    subs => {
      'calcArea():float' => q{
        return 3.1415 * self->radius * self->radius;
      },
    },
  );

The class() method takes as first argument the name of the class. The name has to start with a capitol letter and may be followed by an arbitrary amount of letters, numbers or underscore (to be compatible with the ANSI C standard).

After the first argument the optional parameters follow in any order:

isa => Arrayref of classnames

The isa option lets you specify zero or more parent classes of the class that is to be defined.

attr => Hashref of attributes

The attr option lets you define the attributes of the class that is to be defined.

The hash key is the name of the attribute (starting with a small letter and followed by zero or more letters, numbers or underscore; note: attribute names are case-insensitive).

The hash value is the C-type of the attribute. Here you can use basic C types OR class names (because each class becomes available as a native C type when the C code is generated).

subs => Hashref of methods

The subs option lets you define the methods of the class that is to be defined.

The hash key is the signature of the method, e.g.

  calcArea(float x, MyClass y):int

The hash value is the C sourcecode of the method (s.b. for details). The hash value can optionally be a filename. In this case, the file's content is used as the method's body.

readFile()

readFile() takes one argument, a filename, loads this file and extracts class, attribute and method definitions from it.

  $gen->readFile('c/Triangle.c');

Here is an example file:

  //------------------------------------------------------------------------------
  @class Triangle: Shape, Rectangle
  
  //------------------------------------------------------------------------------
  @attr prop:int
  
  //------------------------------------------------------------------------------
  // calculates the area of the triangle
  //
  @sub calcArea():float
  
  return self->width * self->height;
  
  //------------------------------------------------------------------------------
  // calculates the length of the outline of the triangle
  //
  @sub calcOutline():float
  
  return self->width * 2 + self->height * 2;

A line starting with '//' is ignored. A line that starts with an '@' is treated as a class or attribute definition line or as the start of a method definition. I hope this is self-explanatory?

Such files can be saved with an ".c" extension so that you can open them in your favourite C code editor and have fun with the highlighting.

generate()

  $gen->generate(
    file    => './main.c',
    headers => ['stdio','opengl'],
    main    => 'c/main.c',
    top     => 'c/top.c',
    bottom  => 'c/bottom.c',
  );

The generate() method generates a single ANSI C compliant source file out of the given class definitions.

The optional parameters are:

file => filename

This defines the name of the C output file.

headers => Arrayref of headernames

This defines C headers that are to be included in the generated C file.

main => Source or filename of main function body

This defines the body (C code) of the main function of the generated C file. This can be either C code given as a string OR a filename which is loaded.

top => Source or filename of C code

This method adds arbitrary C code to the generated C file. The code is added after the class structs/typedefs and before the method (function) declarations.

bottom => Source or filename of C code

This method adds arbitrary C code to the generated C file. The code is added to the end of the file, but before the main function.

C programming style

So you have defined a bunch of classes with attributes and methods. But how do you program the method logic in C? This module promises to make it possible to do this in an object-oriented fashion, so this is the section where this fashion is described.

For a more complete example, see the t/ directory in the module dictribution.

Instanciation

Suppose you defined a class named 'Circle'. You can then create an instance of that class like so (C code):

  Circle c = new_Circle();

Destruction

A generic C function delete() is generated which can be used to destruct any object/instance:

  Circle c = new_Circle();
  delete(c); // c now points to NULL

Attribute access

Suppose you defined a class named 'Circle' with an attribute (could also be inherited). Then you can access this attribute the following:

  float r;
  Circle c = new_Circle();
  r = getRadius(c);
  
  setRadius(c, 42.0);

As you can see, all methods (either getter or setter or other ones) need to get the object/instance as first parameter. This "self" parameter need not be written when defining the method, remember to define a method, only the addtional parameters are to be written:

  calcArea(int param):float

Method invocation

To invoke a method on an object/instance:

  Circle c = new_Circle();
  printf("area = %f\n", calcArea(c));

The first argument of the method call is the object/instance the method is invoked on.

Access "self" from within methods

When writing methods you need access to the object instance. This variable is "magically" available and is named "self". Here is an example of a method body:

  printf("radius of instance is %f\n", getRadius(self));

Default attributes

The following attributes are present in all classes:

char* classname

This is the name of the class of the object/instance. To access the classname, use accessor methods like for all other attributes, e.g.:

  Circle c = new_Circle();
  printf("c is of class %s\n", getClassname(c));
  setClassname(c, "Oval");

Beware, that, when you change the classname at runtime, methods may not be able to determine the actual implementation of a method to be applied to an object/instance.

LIMITATIONS

Attribute types

Attributes are not allowed to overwrite attributes of parent classes. This is because, the type of an attribute of a parent class is not allowed to be changed by a child class.

This limitation is due to the way methods are invoked and, to keep the generated ANSI C code typesafe, accessor functions for attributes return the actual C types of the attributes.

Method signatures

If a child class overwrites the method of one of its parent classes, the signatures must be the same, regarding the non-class typed parameters.

To illustrate this, here is an example of a parent class method signature: doSth(Shape s, float f):void - the first parameter is an object of class 'Shape', the second a native C float.

Suppose another classes tries to overwrite this method. In this case the first parameter's type is allowed to change (to any other class type!), but the second not, because its a native type. This will work: doSth(Circle s, float f):void but this not: doSth(int s, float f):void

EXPORT

None by default.

SEE ALSO

Please send any hints on other modules trying to accomplish the same or a similar thing. I haven't found one, yet.

FUTURE PLANS

Implement way to add methods of the same name which differ in their signature (maybe including adding alternative constructors).
        Possible solution:
        
        Allow the definition of methods with same names and differing
        signatures, B<as long as all parameters have class types!>.
        Then, when generating the file, create a wrapper method (which
        is already created now) in which an if-else-if-else construct
        decides which implementation to choose based on the actual
        types of the parameters. Since the wrapper functin can take
        any amount of parameters, it needs to handle the parameters
        using va_args, so a macro with the function name should be
        created which adds a ",NULL" to end of the function call.

AUTHOR

Tom Kirchner, <tom@tomkirchner.com>

COPYRIGHT AND LICENSE

Copyright (C) 2011 by Tom Kirchner

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.0 or, at your option, any later version of Perl 5 you may have available.