Gambas/class authoring

Source file organization
Once the component object model is clearly defined, you can start implementing the classes.

The best method is to have one source file and one header file for each class and its related virtual classes. See below for more information about virtual classes.

Here is an extract from the Qt component source directory: $ pwd ~/gambas/src/lib/qt $ ls CButton.cpp       CIconView.cpp     CScreen.cpp      main.cpp CButton.h        CIconView.h       CScreen.h        main.h  ... You can declare several classes in the same file, but you should generally avoid it. Things are clearer that way.

Virtual classes
What is a virtual class? It is a class which represents a sub-component of a class, but which you cannot instanciate nor reference into a variable. For example, the property Item of the Qt component ListBox class is a virtual class that represents a ListBox item.

Virtual classes are just used as datatypes by the interpreter. But the object used behind is the real object coming from the real non-virtual class. For example, the Item property of the ListBox class stores the index of the item you want to deal with in the ListBox object, and returns this ListBox object. The ListBox object becomes then a virtual class object that you cannot store in a variable. As you must use the virtual class object immediately, by calling a method or a property on it, the stored index will be used immediately too.

This mechanism has been designed so that the user manipulates temporary objects, without being compelled to create them. It is SO much faster!

Note that the name of a virtual class must begin with a dot. For example, the name of the virtual class used by the Item property is .ListBoxItem.

Class source file contents
A class source file contains:
 * The declaration of the class and each related virtual class.
 * The implementation of each method and properties.
 * Any static function, variable or class (in C++) related to the class implementation.

Header contents
The structure of the class header file is as follows: /* MyClass.h */

#ifndef __MYCLASS_H #define __MYCLASS_H Include main.h there, and any other includes needed by the declarations located in this file. #include "main.h" If the class is instanciable, declare the structure of your objects. Note that the structure must begin with a GB_BASE field, and that the other fields are free. typedef struct { GB_BASE ob; ...     }    MyClassStruct; main.c will include the class include files, so it must have an access to the class description. #ifndef __CEXAMPLE_C extern GB_DESC MyClass[]; extern GB_DESC MyVirtualClassDesc[]; Otherwise, you can declare useful macros that helps writing the class implmentation. For example, the following constant makes the code more readable. Note that it can only be used where _object is declared, i.e. inside a method or property implementation. #else #define THIS ((MyClassStruct *)_object) #endif

#endif /* __MYCLASS_H */

Source contents
The structure of the class source file is as follows: /* MyClass.c */

#define __CEXAMPLE_C Include the class header file, and any other include files needed by the contents of the source file. #include #include ...

#include "MyClass.h" If your class raises events, you must declare them with the DECLARE_EVENT macro. DECLARE_EVENT(FirstEvent); DECLARE_EVENT(SecondEvent); ... Include any static functions that may be needed by your implementation. static void do_job(...) {  ...  } Then write the implementation of each method and property. BEGIN_METHOD(...) ... END_METHOD

BEGIN_PROPERTY(...) ... END_METHOD Finally, the last part of the class source file is the declaration of its description. A class description is an array of GB_DESC structure filled with special macros declared in gambas.h  GB_DESC MyClassDesc[] = {   GB_DECLARE("MyClass", sizeof(MyClassStruct)), ...   GB_END_DECLARE };

Class description
The class description must begin with a GB_DECLARE macro and end with a GB_END_DECLARE macro.

Class declaration
Use the GB_DECLARE macro to declare the name of the class and the size of its object. If the class is virtual, add a line with the GB_VIRTUAL_CLASS macro. Don't forget that the class name must begin with a dot, and the size of the object must be equal to zero.

If the class is normal, but not creatable by the user, add a line with the GB_NOT_CREATABLE macro. Then you add a declaration line for each class symbol.

Inheritance
Your class can inherit from another class, by using the GB_INHERITS macro and specifying the parent class name. A class inherits from its parent all methods, properties, constants and events. Moreover, the class object structure must include the parent object structure at its beginning. Otherwise, the inheritance will not work!

Example:
The TreeView class inherits from the Control.

The Control object structure is as follows: typedef struct { GB_BASE ob; QWidget *widget; ...     }    CWIDGET; And the TreeView object structure is as follows: typedef struct { CWIDGET widget; ...     }    CTREEVIEW;

Constants
A constant is depicted by its name, its type and its value.

To declare a constant, use the GB_CONSTANT macro.

See the macro description for more information.

Properties
A property is depicted by its name, its type, and its implementation function.

To declare a property, use the GB_PROPERTY macro, or the GB_STATIC_PROPERTY macro if the property is static.

If the property is read-only, you must use the GB_PROPERTY_READ or GB_STATIC_PROPERTY_READ macro instead.

If you want a special property that returns the same object as a virtual class, use the GB_PROPERTY_SELF or GB_STATIC_PROPERTY_SELF macro.

See the macro description for more information.

Methods
A method is depicted by its name, its return type, its signature and its implementation function.

To declare a method, use the GB_METHOD macro, or the GB_STATIC_METHOD macro if the method is static.

See the macro description for more information.

Events
An event is depicted by its name, its return type, and its signature.

To declare an event, use the GB_EVENT macro. This macro takes a variable pointer as an argument to let the interpreter allocate an identifier to this event. This variable must be declared in the source file with the DECLARE_EVENT macro.

See the macro descriptions for more information.

Implementing a method
To implement a method, you must write a function whose code is enclosed between the two macros: BEGIN_METHOD and END_METHOD.

The BEGIN_METHOD macro takes TWO arguments: the function name, and a list of arguments separated by semicolons.

The method arguments are NOT separated by commas, because they are in reality fields of a structure passed to the function.

If your method takes no argument, you must use the BEGIN_METHOD_VOID instead of BEGIN_METHOD. The BEGIN_METHOD_VOID macro takes only one argument, the name of the function.

The gambas.h include file contains definitions for the argument types: You MUST use these datatypes!
 * GB_BOOLEAN for a boolean argument.
 * GB_INTEGER for an integer argument.
 * GB_FLOAT for a double argument.
 * GB_STRING for a string argument.
 * GB_DATE for a date argument.
 * GB_VARIANT for a variant argument.
 * GB_OBJECT for a object reference.

To get the parameters, you have two macros : ARG and VARG.

The ARG macro returns the address of the parameter in the interpreter stack, and is used with functions like GB.ToZeroString, or GB.Store.

The VARG macro returns the value of the parameter.

Example :
BEGIN_METHOD ( TheFunctionName, GB_INTEGER anInteger; GB_STRING aString; GB_VARIANT aVariant; GB_BOOLEAN aBoolean; ) To get the value of a parameter, you must use the VARG macro. printf("anInteger = %d\n", VARG(anInteger)); To get a string parameter, you must use the special macros STRING and LENGTH to get the address of the string and its length. printf("aString = %*.s\n", LENGTH(aString), STRING(aString)); You can also transform the Gambas string into a C zero-terminated string with the GB.ToZeroString function. You must use the ARG macro to get the parameter address, and not the VARG macro that returns its value. printf("aString = %.s\n", GB.ToZeroString(ARG(aString))); GB_VARIANT is a union of different datatypes. The type field of this union is one of the GB_T_* constants. if (VARG(aVariant).type == GB_T_STRING) printf("I got the following string: %s\n", VARG(aVariant)._string.value); GB_BOOLEAN is stored as an integer, which is zero when FALSE, and different from zero when TRUE. printf("aBoolean = %s\n", VARG(aBoolean) ? "TRUE" : "FALSE"); If you want to raise an error into your method, you must use the GB.Error function to register the error, and returns immediately after. if (VARG(aBoolean)) {     GB.Error("There was an error!"); return; } To return a value from the method, you can use the GB.Return interface functions: GB.ReturnInteger to return a integer, GB.ReturnBoolean to return a boolean, and so on. GB.ReturnInteger(VARG(anInteger) * 2);

END_METHOD

Implementing a property
To implement a property, you must write a function whose code is enclosed between the two macros : BEGIN_PROPERTY and END_PROPERTY.

The BEGIN_PROPERTY macro takes one argument: the property name.

The function is called both for reading and writing the property. To distinguish between the two cases, you must use the READ_PROPERTY macro. Of course, if your property is read-only, this is not necessary.

When reading the property, you must return the property value with one of the GB.Return functions.

When writing the property, you get the value to write with the VPROP macro. This macro takes one argument: the datatype of the property, which must be one of the Gambas value structure defined in gambas.h : Use the PROP macro to get the address of the value, if you want to use functions like like GB.ToZeroString, or GB.Store.
 * GB_BOOLEAN for a boolean argument.
 * GB_INTEGER for an integer argument.
 * GB_FLOAT for a double argument.
 * GB_STRING for a string argument.
 * GB_DATE for a date argument.
 * GB_VARIANT for a variant argument.
 * GB_OBJECT for a object reference.

Example:
BEGIN_PROPERTY ( ThePropertyName ) First, you must check if we want to read or to write the property. if (READ_PROPERTY) { Here we are reading the property...      printf("Returning the property value\n"); The THIS macro is defined in the class header file. It returns a pointer to the data of the object structure.

We suppose here that there is a char * AStringProperty defined in the object structure, that points to a Gambas string. GB.ReturnString(THIS->AStringProperty); }   else { Here we are writing the property...

To store a complex Gambas datatype like String or Object, you must use GB.StoreString or GB.StoreObject. These functions deal with reference counting. printf("I'm going to write the value: %s\n", GB.ToZeroString(PROP(GB_STRING)));

GB.StoreString(PROP(GB_STRING), &THIS->AStringProperty); Generally, a modified property implies some other actions...      printf("Property has been modified. The new value is %s\n", THIS->AStringProperty); }

END_PROPERTY