blob: 2890b30c13b7cfa933f7261826e9626d4a1ab7c5 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
=head1 NAME
Clownfish - Symbiotic object system.
=head1 VERSION
0.4.1
=head1 DESCRIPTION
Clownfish is a "symbiotic" object system for C which is designed to pair
with a "host" dynamic language environment, facilitating the development
of high performance host language extensions. Clownfish classes are
declared in header files with a C<.cfh> extension. The Clownfish headers
are used by the Clownfish compiler to generate C header files and host
language bindings. Methods, functions and variables are defined in normal
C source files.
=head2 Features
=over
=item *
Designed to support multiple host languages. Currently, only Perl is supported.
=item *
Support for stand-alone C libraries and executables.
=item *
Subclassing and method overriding from the host language.
=item *
Support for host language idioms like named parameters or default argument
values.
=item *
Highly performant object system with lazy creation of host language objects.
=item *
Runtime with classes for commonly used data structures like strings, dynamic
arrays and hash tables.
=item *
Modularity.
=item *
Introspection.
=item *
Documentation generator.
=back
=head2 Planned features
=over
=item *
Support for more host languages.
=item *
Guaranteed ABI stability when adding or reordering methods or instance
variables.
=back
=head1 USING CLOWNFISH CLASSES
TODO: Simple introduction
=head1 WRITING CLOWNFISH CLASSES
=head2 Parcels
Every Clownfish class belongs to a Clownfish parcel. Parcels are used for
namespacing and versioning. Information about parcels is stored in C<.cfp>
files which contain a JSON hash with the following keys:
=over
=item name
The parcel's name. It must contain only letters.
=item nickname
A short nickname. It must contain only letters. This nickname, followed by an
underscore, is used to prefix generated C symbols and macros. Depending on the
kind of symbol, a lowercase, mixed case, or uppercase prefix will be used.
=item version
A version specifier of the following form (without whitespace):
version-specifier = "v" version-number
version-number = digit | digit "." version-number
=back
An example C<.cfp> file might look like:
{
"name": "Pathfinder",
"nickname": "Pfind",
"version": "v2.3.8"
}
A parcel specifier of the following form is used in Clownfish header files:
parcel-specifier = "parcel" parcel-name ";"
parcel-name = identifier
For example:
parcel Pathfinder;
All classes following a parcel specifier will be associated with that parcel.
=head3 Initialization
Every Clownfish parcel must be initialized before it is used. The
initialization function is named C<{parcel_nick}_bootstrap_parcel> and takes
no arguments.
Example call:
pfind_bootstrap_parcel();
=head3 Short names
If a macro with the uppercase name C<{PARCEL_NICK}_USE_SHORT_NAMES> is
defined before including a generated C header, additional macros without the
parcel prefix will be defined for most symbols.
Example:
#define PFIND_USE_SHORT_NAMES
#include <Pathfinder/Graph.h>
#include <Pathfinder/Path.h>
/* Prefixes can be omitted. */
Path *path = Graph_Find_Shortest_Path(graph);
/* Without PFIND_USE_SHORT_NAMES, one would have to write: */
pfind_Path *path = Pfind_Graph_Find_Shortest_Path(graph);
For object types in Clownfish header files, prefixes of class structs can
also be omitted unless multiple parcels declare classes with the same last
component of the class name.
=head3 The "Clownfish" parcel
The Clownfish runtime classes live in a parcel named C<Clownfish> with
nickname C<Cfish>. Consequently, the short name macro is named
C<CFISH_USE_SHORT_NAMES>.
=head2 Declaring classes
Classes are declared in Clownfish header files using a declaration of the
following form:
class-declaration = class-exposure-specifier?
class-modifier*
"class" class-name
("nickname" class-nickname)?
("inherits" class-name)?
"{" class-contents "}"
class-exposure-specifier = "public"
class-modifier = "inert" | "final"
class-name = identifier | identifier "::" class-name
class-nickname = identifier
class-contents = (variable-declaration | function-declaration)*
Class name components must start with an uppercase letter and must not contain
underscores. The last component must contain at least one lowercase letter and
must be unique for every class in a parcel.
For every class, a struct with the name C<{parcel_nick}_{Class_Last_Comp}>
and a corresponding typedef are declared in the generated C header. The struct
members are only visible if the uppercase macro
C<C_{PARCEL_NICK}_{CLASS_LAST_COMP}> is defined before including the header
file.
For every class, a global variable with the uppercase name
C<{PARCEL_NICK}_{CLASS_LAST_COMP}> is defined. This variable is a pointer to
a Clownfish::Class object which is initialized when bootstrapping the parcel.
Example of a class declaration:
parcel Pathfinder;
public class Pathfinder::Graph::VisibilityGraph nickname VisGraph
extends Clownfish::Obj {
/* Variables and functions */
}
This will generate:
struct pfind_VisibilityGraph {
/* Instance variables */
};
typedef struct pfind_VisibilityGraph pfind_VisibilityGraph;
extern cfish_Class *PFIND_VISIBILITYGRAPH;
=head3 Class exposure
Classes without public exposure have parcel exposure. They are not visible
outside of the parcel and must not contain public variables or functions.
=head3 Inert classes
Inert classes must contain only inert variables or inert methods, that is,
neither instance variables nor methods. They must not inherit from another
class or be inherited from. Non-inert classes inherit from Clownfish::Obj by
default.
=head3 Final classes
For final classes, every method is made final, regardless of the method
modifier. Final classes must not be inherited from.
=head2 Variables
Variables are declared with a declaration of the following form:
variable-declaration = variable-exposure-specifier?
variable-modifier*
type variable-name ";"
variable-exposure-specifier = "public"
variable-modifier = "inert"
variable-name = identifier
=head3 Inert variables
Inert variables are class variables of which only a single copy exists.
They are declared in the generated C header with the name
C<{parcel_nick}_{Class_Nick}_{Variable_Name}> and must be defined in a C
source file.
Example:
public class Pathfinder::Path {
public inert int max_path_length;
}
This will generate:
extern int pfind_Path_max_path_length;
The C source file defining the variable will typically use short names. So the
definition will look like:
int Path_max_path_length = 5000;
=head3 Inert variable exposure
Inert variables without public exposure have parcel exposure. They are not
visible outside of the parcel.
=head3 Instance variables
Non-inert variables are instance variables and added to the class struct. They
must not have an exposure specifier.
Example:
public class Pathfinder::Path {
int num_nodes;
public int
Get_Num_Nodes(Path *self);
}
This will add a C<num_nodes> member to struct C<pfind_Path>. To access the
member, the macro C<C_PFIND_PATH> must be defined before including the
generated header file:
#define C_PFIND_PATH
#define PFIND_USE_SHORT_NAMES
#include "Pathfinder/Path.h"
int
Path_get_num_nodes(Path *self) {
return self->num_nodes;
}
=head2 Functions
function-declaration = function-exposure-specifier?
function-modifier*
return-type function-name
"(" param-list? ")" ";"
function-exposure-specifier = "public"
function-modifier = "inert" | "inline" | "abstract" | "final"
return-type = return-type-qualifier* type
return-type-qualifier = "incremented" | "nullable"
function-name = identifier
param-list = param | param "," param-list
param = param-qualifier* type param-name ("=" scalar-constant)?
param-name = identifier
param-qualifier = "decremented"
=head3 Function exposure
Functions without public exposure have parcel exposure. They are not
visible outside of the parcel.
=head3 Inert functions
Inert functions are dispatched statically. They are declared in the generated
C header with the name C<{parcel_nick}_{Class_Nick}_{Function_Name}>
and must be defined in a C source file. They must be neither abstract nor
final.
Example:
public class Pathfinder::Graph::VisibilityGraph nickname VisGraph
extends Clownfish::Obj {
public inert incremented VisibilityGraph*
new(int node_capacity);
}
This will generate:
pfind_VisibilityGraph*
pfind_VisGraph_new(int node_capacity);
The C source file implementing the inert function will typically use short
names. So the implementation will look like:
#define PFIND_USE_SHORT_NAMES
#include "Pathfinder/Graph/VisibilityGraph.h"
VisibilityGraph*
VisGraph_new(int node_capacity) {
/* Implementation */
}
=head3 Inline functions
Inert functions can be inline. They should be defined as static inline
functions in a C block in the Clownfish header file. The macro C<CFISH_INLINE>
expands to the C compiler's inline keyword and should be used for portability.
=head3 Methods
Non-inert functions are dynamically dispatched methods. Their name must start
with an uppercase letter and every underscore must be followed by an uppercase
letter. Methods must not be declared inline.
The first parameter of a method must be a pointer to an object of the method's
class which receives the object on which the method was invoked. By convention,
this parameter is named C<self>.
For every method, an inline wrapper for dynamic dispatch is defined in
the generated C header with the name
C<{Parcel_Nick}_{Class_Nick}_{Method_Name}>. Additionally, an
implementing function is declared with the name
C<{Parcel_Nick}_{Class_Nick}_{Method_Name}_IMP>. The Clownfish compiler also
generates a typedef for the method's function pointer type named
C<{Parcel_Nick}_{Class_Nick}_{Method_Name}_t>. Wrappers and typedefs are
created for all subclasses whether they override a method or not.
Example:
public class Pathfinder::Graph::VisibilityGraph nickname VisGraph
extends Clownfish::Obj {
public void
Add_Node(VisibilityGraph *self, decremented Node *node);
}
This will generate:
/* Wrapper for dynamic dispatch */
static inline void
Pfind_VisGraph_Add_Node(pfind_VisibilityGraph *self, pfind_Node *node) {
/* Inline code for wrapper */
}
/* Declaration of implementing function */
void
Pfind_VisGraph_Add_Node_IMP(pfind_VisibilityGraph *self,
pfind_Node *node);
/* Declaration of function pointer type */
typedef void
(*Pfind_VisGraph_Add_Node_t)(pfind_VisibilityGraph *self,
pfind_Node *node);
The implementing function of non-abstract methods must be defined in a C source
file. This file will typically define the short names macro. So the
implementation will look like:
#define PFIND_USE_SHORT_NAMES
#include "Pathfinder/Graph/VisibilityGraph.h"
void
VisGraph_Add_Node_IMP(VisibilityGraph *self, Node *node) {
/* Implementation */
}
=head3 Looking up function pointers
Clownfish defines a macro named C<CFISH_METHOD_PTR> that looks up the pointer
to the implementing function of a method. The first parameter of the macro is
a pointer to the Clownfish::Class object of the method's class, the second is
the unshortened name of the method wrapper. If short names for the Clownfish
parcel are used, the macro is also available under the name C<METHOD_PTR>.
To lookup methods from a superclass, there's a macro C<CFISH_SUPER_METHOD_PTR>
with the same parameters.
Example using short names:
VisGraph_Add_Node_t add_node
= METHOD_PTR(PFIND_VISIBILITYGRAPH, Pfind_VisGraph_Add_Node);
VisGraph_Add_Node_t super_add_node
= SUPER_METHOD_PTR(PFIND_VISIBILITYGRAPH, Pfind_VisGraph_Add_Node);
=head3 Abstract methods
For abstract methods, the Clownfish compiler generates an implementing function
which throws an error. They should be overridden in a subclass.
=head3 Final methods
Final methods must not be overridden. They must not be abstract.
=head3 Nullable return type
If a function has a nullable return type, it must return a pointer.
Non-nullable functions must never return the NULL pointer.
=head3 Incremented return type
Incremented return types must be pointers to Clownfish objects. The function
will either return a new object with an initial reference count of 1 or
increment the reference count. The caller must decrement the reference count of
the returned object when it's no longer used.
For returned objects with non-incremented return type, usually no additional
handling of reference counts is required. Only if an object is returned from an
accessor or a collection object and the caller wants to use the object longer
than the returning object retains a reference, it must increment the reference
count itself and decrement when the object is no longer used.
=head3 Decremented parameters
Decremented parameters must be pointers to Clownfish objects. The function
will either decrement the reference count of the passed-in object or retain a
reference without incrementing the reference count. If the caller wants to use
the passed-in object afterwards, it usually must increment its reference count
before the call and decrement it when it's no longer used. If the caller does
not make further use of the passed-in object, it must not decrement its
reference count after the call.
=head3 Default parameter values
Default parameter values can be given as integer, float, or string literals.
The values C<true>, C<false>, and C<NULL> are also supported. The default
values are only used by the host language bindings. They're not supported
when calling a function from C.
=head2 C blocks
Clownfish headers can contain C blocks which start with a line containing the
string C<__C__> and end on a line containing the string C<__END_C__>. The
contents of a C block are copied verbatim to the generated C header.
Example:
__C__
struct pfind_AuxiliaryStruct {
int a;
int b;
};
__END_C__
=head2 Object life cycle
=head3 Object creation
Objects are allocated by invoking the C<Make_Obj> method on a class's
Clownfish::Class object.
Any inert function can be used to construct objects from C. But to support
inheritance and object creation from the host language, Clownfish classes
need a separate function to initialize objects. The initializer must take a
pointer to an object as first argument and return a pointer to the same
object. If the parent class has an initializer, it should be called first by
the subclass's initializer.
By convention, the standard constructor is named C<new>. If a class has an
inert function named C<init>, it is used as initializer to create a host
language constructor by default.
Example:
/* Clownfish header */
class Vehicle {
double max_speed;
inert Vehicle*
init(Vehicle *self, double max_speed);
}
class Train inherits Vehicle {
double track_gauge;
inert incremented Train*
new(double max_speed, double track_gauge);
inert Train*
init(Train *self, double max_speed, double track_gauge);
}
/* Implementation */
Train*
Train_new(double max_speed, double track_gauge) {
Train *self = (Train*)Class_Make_Obj(TRAIN);
return Train_init(self, max_speed, track_gauge);
}
Train*
Train_init(Train *self, double max_speed, double track_gauge) {
Vehicle_init((Vehicle*)self, max_speed);
self->track_gauge = track_gauge;
return self;
}
=head3 Reference counting
Clownfish uses reference counting for memory management. Objects are created
with a reference count of 1. There are two macros C<CFISH_INCREF> and
C<CFISH_DECREF> to increment and decrement reference counts. If short names
for the Clownfish parcel are enabled, the macros can be abbreviated to
C<INCREF> and C<DECREF>. Both macros take a pointer to an object as argument.
NULL pointers are allowed. C<CFISH_INCREF> returns a pointer to the object.
This value might differ from the passed-in pointer in some cases. So if a
reference is retained, the pointer returned from C<CFISH_INCREF> should be
used. C<CFISH_DECREF> returns the modified reference count.
Examples:
self->value = INCREF(arg);
DECREF(object);
=head3 Object destruction
If an object's reference count reaches 0, its C<Destroy> method is called.
This public method takes no arguments besides C<self> and has no return value.
It should release the resources held by the object and finally call the
C<Destroy> method of the superclass via the C<CFISH_SUPER_DESTROY> macro with
short name C<SUPER_DESTROY>. This macro takes the C<self> pointer as first
argument and a pointer to the object's Clownfish::Class as second argument.
The C<Destroy> method of the Clownfish::Obj class will eventually free the
object struct.
Example:
/* Clownfish header */
class Path {
VArray *nodes;
public void
Destroy(Path *self);
}
/* Implementation */
void
Path_Destroy_IMP(Path *self) {
DECREF(self->nodes);
SUPER_DESTROY(self, PATH);
}
=head1 COPYRIGHT
Clownfish is distributed under the Apache License, Version 2.0, as described
in the file C<LICENSE> included with the distribution.
=cut