Interface
The functional interface of So-o en C consists of 3 functions:
defclass
which defines a new class, sendmsg
which is systematically used to send a message to a class or an instance, and supersend
which runs a method inherited from a superclass.
All the code for the So-o interface is in the file So-o.c.
- #ifndef _SOO_H
- #define _SOO_H
- #include "Object.h"
- #ifdef __STDC__
- extern class defclass( const char *name, class superclass, unsigned revision, property *class_properties, property *instance_properties, selector *class_messages, selector *instance_messages );
- extern value sendmsg( void *rcv, message msg, ... );
- extern value applymsg( void *rcv, message msg, va_list va);
- extern value supersend( class c, void *rcv, message msg, ... );
- extern value superapply( class c, void *rcv, message msg, va_list va );
- #else
- extern class defclass();
- extern value sendmsg();
- extern value applymsg();
- extern value supersend();
- extern value superapply();
- #endif
- #endif
The file So-o.h declares the functions of the interface of So-o. It includes the file Object.h which includes the file OL.h.
defclass
SYNOPSIS
class defclass( const char *name, class superclass, unsigned revision, property *class_properties, property *instance_properties, selector *class_messages, selector *instance_messages )
DESCRIPTION
defclass
defines a class.
name
specifies the name of the class. name
must be a valid C variable name.
superclass
is the global reference of the superclass of the new class.
If superclass
is 0
, the new class inherits by default from the Object class defined by the global variable Object
.
defclass
automatically builds the class Object if necessary.
revision
gives the revision number of the class. revision
is an int
> 0 which can be used to differentiate successive versions of the class.
class_properties
and instance_properties
list the properties of the class and instances of the class.
A property is a character string terminated by a '\0'
.
A list of properties is an array terminated by a NULL
pointer.
A NULL
array defines an empty list.
class_messages
and instance_messages
are associative lists of messages and methods of the class and instances of the class.
A message is a character string terminated by a '\0'
.
A method is a function pointer cast with the macro METHOD
defined in OL.h.
A selector associates a method to a message.
A list of messages and methods is an array terminated by two NULL
pointers.
A NULL
array defines an empty list.
defclass
returns a pointer to an allocated struct _class
which must be assigned to the global variable name
.
Notice that a class can be redefined.
A new class automatically receives the message initialize.
NOTE: The initialize
class method defined by the Object class does nothing.
EXAMPLE
- #include "So-o.h"
Includes the declarations of the So-o functions and the class Object
.
- class Hello;
Defines the class Hello, a global variable.
- static instance i_hello(instance self) {
- printf( "Hello from So-o!\n" );
- return self;
- }
Defines the code of the instance message hello.
Conventionally, a method is a function whose name is the instance message prefixed by i_
for an instance method and by c_
for a class method.
A class or an instance method is static.
The first argument of a method is always the class or the instance which receives the message.
Conventionally, this argument is called self
.
A method which has nothing in particular to return generally returns self
.
- void defclassHello() {
- selector _i_messages[] = {
- "hello", METHOD(i_hello),
- 0, 0
- };
- Hello = defclass("Hello", 0, 1, 0, 0, 0, _i_messages);
- }
Builds the class Hello and associates it to the global variable Hello
.
The Hello class inherits by default from the Object class.
Its revision number is 1.
It doesn't have any class properties, class messages or instance properties.
It has an instance message: hello.
- #include "So-o.h"
- #include <stdlib.h>
- extern class Hello;
- extern void defclassHello();
- int main( int argc, char *argv[] ) {
- instance hello;
- defclassHello();
- hello = (instance)sendmsg(Hello, "new").p;
- sendmsg(hello, "hello");
- sendmsg(hello, "free");
- exit( 0 );
- }
Includes the declarations of the functions and data types of So-o.
Declares the class Hello and its constructor.
Initializes the class Hello.
Sends the message new to the class Hello to create the instance hello
assigning to it the pointer of the union returned by sendmsg
.
Sends the message hello to the instance hello
to display the welcome message.
Sends the message free to the instance hello
to free the space allocated by this instance.
$ gcc -O -c test-Hello.c
$ gcc -O -c Hello.c
$ gcc test-Hello.o Hello.o libso-o.a -o test-Hello
$ test-Hello
Hello from So-o!
CODE
- class defclass( const char *name, class superclass, unsigned revision, property *class_properties, property *instance_properties, selector *class_messages, selector *instance_messages ) {
defclass
takes 7 arguments.
name
specifies the name of the class.
superclass
is the global reference of the superclass of the new class.
revision
gives the revision number of the class.
class_properties
, instance_properties
, class_messages
and instance_messages
list the properties and the selectors of the class and the instances of the class.
- if (!Object)
- defclassObject();
Builds the class Object if the global variable Object
is 0.
- class c = class_new(name, superclass, revision, class_properties, instance_properties, class_messages, instance_messages);
Creates a struct _class
with the function class_new
defined by the Object Layer with the parameters of the call to defclass
.
- class_send_message(c, "initialize");
Sends the message initialize to the new class.
- return c;
- }
Returns the class.
sendmsg
SYNOPSIS
value sendmsg( void *rcv, message msg, ... )
DESCRIPTION
sendmsg
returns the result of sending the message msg
and its parameters to the instance or the class rcv
.
The return type of sendmsg
is value
, a union.
CODE
- value sendmsg( void *rcv, message msg, ... ) {
- va_list va;
- va_start(va, msg);
- value ret = applymsg(rcv, msg, va);
- va_end(va);
- return ret;
- }
sendmsg
builds the variable va
of type va_list
with the parameters following msg
and calls applymsg
with rcv
, msg
and va
in argument.
applymsg
SYNOPSIS
value applymsg( void *rcv, message msg, va_list va)
DESCRIPTION
applymsg
returns the result of sending the message msg
with the parameters in
va to the class or the instance rcv
.
Calling applymsg
directly is an option which can be vital when a code must pass arguments between methods.
CODE
- value applymsg( void *rcv, message msg, va_list va ) {
- #ifdef TRYCATCH
- static jmp_buf toplevel;
- static unsigned calldepth = 0;
- if (calldepth == 0) {
- if (setjmp(toplevel) != 0) {
- calldepth = 0;
- fprintf(stderr, BadReceiver, "sendmsg", msg, rcv);
- fprintf(stderr, "\n");
- return (value)0;
- }
- }
- if (!rcv)
- longjmp(toplevel, 1);
- ++calldepth;
- #endif
- value ret;
- switch (((instance)rcv)->type) {
- case INSTANCE:
- ret = object_send_message_va(rcv, msg, va);
- break;
- case CLASS:
- ret = class_send_message_va(rcv, msg, va);
- break;
- default:
- #ifdef TRYCATCH
- longjmp(toplevel, 1);
- #else
- ret = (value)0;
- break;
- #endif
- }
- #ifdef TRYCATCH
- --calldepth;
- #endif
- return ret;
- }
applymsg
calls either class_send_message
or object_send_message
depending on the type of rcv
, CLASS
or INSTANCE
, with rcv
, msg
and va
in argument.
applymsg
returns the value returned class_send_message
or object_send_message
.
When compiled with TRYCATCH
, applymsg
detects if rcv
is NULL
or is not a class nor an instance and makes the code jump back to a safe point just before the first call to applymsg
in the stack.
This mode is useful to analyze a program which craches because of a memory fault probably caused by a faulty reference to a class or an instance.
supersend
SYNOPSIS
value supersend( class c, void *rcv, message msg, ... )
DESCRIPTION
supersend
returns the result of sending the message msg
and its parameters to the instance or the class rcv
in the context of the superclass of the class c
.
The return type of sendmsg
is value
, a union.
supersend
is generally used to call the inherited version of a method which is redefined by a subclass.
In this case, c
is the class of the method which redefines msg
and calls supersend
.
CODE
- value supersend( class c, void *rcv, message msg, ... ) {
- va_list va;
- va_start(va, msg);
- value ret = superapply( c, rcv, msg, va );
- va_end(va);
- return ret;
- }
supersend
builds the variable va
of type va_list
with the parameters following msg
and calls superapply
with c
, rcv
, msg
and va
in argument.
superapply
SYNOPSIS
value superapply( class c, void *rcv, message msg, va_list va)
DESCRIPTION
superapply
returns the result of sending the message msg
with the parameters in
va to the class or the instance rcv
in the context of the superclass of the class c
.
Calling superapply
directly is an option which can be vital when a code must pass arguments between methods.
CODE
- value superapply( class c, void *rcv, message msg, va_list va ) {
- #ifdef TRYCATCH
- static jmp_buf toplevel;
- static unsigned calldepth = 0;
- if (calldepth == 0) {
- if (setjmp(toplevel) != 0) {
- calldepth = 0;
- fprintf(stderr, BadReceiver, "supersend", msg, rcv);
- fprintf(stderr, "\n");
- return (value)0;
- }
- }
- if (!c)
- longjmp(toplevel, 1);
- if (!rcv)
- longjmp(toplevel, 1);
- ++calldepth;
- #endif
- value ret;
- switch (((instance)rcv)->type) {
- case INSTANCE:
- ret = object_super_send_message_va(c, rcv, msg, va);
- break;
- case CLASS:
- ret = class_super_send_message_va(c, rcv, msg, va);
- break;
- default:
- #ifdef TRYCATCH
- longjmp(toplevel, 1);
- #else
- ret = (value)0;
- break;
- #endif
- }
- #ifdef TRYCATCH
- --calldepth;
- #endif
- return ret;
- }
superapply
calls either class_super_send_message
or object_super_send_message
depending on the type of rcv
, CLASS
or INSTANCE
, with c
, rcv
, msg
and va
in argument.
applymsg
returns the value returned class_send_message
or object_send_message
.
When compiled with TRYCATCH
, applymsg
detects if c
is NULL or if rcv
is NULL
or is not a class nor an instance and makes the code jump back to a safe point just before the first call to superapply
in the stack.
This mode is useful to analyze a program which craches because of a memory fault probably caused by a faulty reference to a class or an instance.
EXAMPLE
The first argument of a method is always the class or the instance which receives the message. If a method expects parameters, it must extract them from the va_list
which the Object Layer receives from applymsg
or superapply
and passes to a method as a second argument.
A method can return one of the data types which is a member of the union value
defined in OL.h.
To assign to a variable or directly pass the value returned by the processing of a message, the code must access the member of the union corresponding to the expected data type.
int i;
long l;
float f;
double d;
void *p;
} value;
- #include "So-o.h"
- class X;
Includes the declarations of the functions and data types of So-o. Defines the class X, a global variable.
- static class c_initialize(class self) {
- return sendmsg(self, "set", "count", 0).p;
- }
c_initialize
initializes the property count of the class to 0 when the class is constructed.
NOTE: The initialize message is automatically sent to a new class by defclass
.
- static instance c_new(class self, va_list va) {
- instance i = superapply(X, self, "new", va).p;
- sendmsg(self, "set", "count", sendmsg(self, "get", "count").i + 1);
- return i;
- }
c_new
creates an instance of X by sending the message new in the context of the Object class, the superclass of the X class, then increments count before returning the new instance.
Notice how the class which implements the method and the argument list are passed to superapply
.
- static int c_count(class self) {
- return sendmsg(self, "get", "count").i;
- }
c_count
returns the number of instances of X.
- static void i_free(instance self) {
- class c = sendmsg(self, "class").p;
- sendmsg(c, "set", "count", sendmsg(c, "count").i - 1);
- supersend(X, self, "free");
- }
i_free
decrements count then executes the message in the context of the Object class.
Notice how an instance method sends a message to its class.
- static instance i_init(instance self, va_list va) {
- int value = va_arg(va, int);
- supersend(X, self, "init");
- sendmsg(self, "set", "value", value);
- return self;
- }
i_init
executes the init message in the context of the Object class then initializes value with the parameter value
of the message.
The parameters of a method are always passed in a va_list
.
NOTE: The init message is automatically sent to a new instance by new.
- static value i_value(instance self) {
- return sendmsg(self, "get", "value");
- }
i_value
returns the value of an instance of X.
- void defclassX() {
- property _c_properties[] = {
- "count",
- 0
- };
- property _i_properties[] = {
- "value",
- 0
- };
- selector _c_messages[] = {
- "initialize", METHOD(c_initialize),
- "new", METHOD(c_new),
- "count", METHOD(c_count),
- 0, 0
- };
- selector _i_messages[] = {
- "free", METHOD(i_free),
- "init", METHOD(i_init),
- "value", METHOD(i_value),
- 0, 0
- };
- X = defclass("X", 0, 1, _c_properties, _i_properties, _c_messages, _i_messages);
- }
Initializes the class X. The class X inherits from the class Object. It has 1 property: count, the number of instances it has created. An instance has 1 property: value, its value. The class X redefines the messages initialize and new. It implements the message count. An instance redefines the messages free and init. It implements the message value.
TEST
- #include "So-o.h"
- #include <stdlib.h>
- extern class X;
- extern void defclassX();
- int main( int argc, char *argv[] ) {
- instance x1, x2;
- defclassX();
- printf("count=%d\n", sendmsg(X, "count").i);
- x1 = (instance)sendmsg(X, "new", 1).p;
- printf("x1=%d\n", sendmsg(x1, "value").i);
- x2 = (instance)sendmsg(X, "new", 2).p;
- printf("x2=%d\n", sendmsg(x2, "value").i);
- printf("count=%d\n", sendmsg(X, "count").i);
- sendmsg(x1, "free");
- printf("count=%d\n", sendmsg(X, "count").i);
- sendmsg(x2, "free");
- printf("count=%d\n", sendmsg(X, "count").i);
- exit( 0 );
- }
Initializes the class X. Displays the count of instances. Creates an instance of X with the value 1. Displays its value. Creates an instance of X with the value 2. Displays its value. Displays the count of instances. Frees the first instance. Displays the count of instances. Frees the second instance. Displays the count of instances.
$ gcc -I/usr/local/include/so-o X.c test-X.c -lso-o -o test-X
$ test-X
count=0
x1=1
x2=2
count=2
count=1
count=0
Comments