Написание классов

Организация исходного файла

Когда модель объекта компонента ясно определена, вы можете начинать реализацию классов.

Наилучший метод - получить по одному исходному файлу и одному файлу заголовков (header file) для каждого класса и относящихся к нему виртуальных классов. Посмотрите ниже, где больше инофрмации по виртуальным классам.

Вот выдержка из Qt компонента исходной директории:

  $ pwd
~/gambas/src/lib/qt
$ ls
CButton.cpp CIconView.cpp CScreen.cpp main.cpp
CButton.h CIconView.h CScreen.h main.h
...

Вы может объявить несколько классов в том же самом файле, но вы должны обычно избегать этого. Все становится яснее, если следовать этому.

Виртуальные классы

Что такое виртуальный класс? Это класс, который представляет суб-компонент класса, но которому вы не можете ни приписать значения, ни сослаться на него в переменной. Например, свойство Item компонента Qt класса ListBox - это виртуальный класс, который представляет ListBox раздел (item).

Виртуальные классы используются только интерпретатором, как типы данных. Но объект, используемый за ним, это реальный объект, приходящий из реального, не виртуального класса. Например, свойство Item класса ListBox сохраняет индекс раздела (item), с которым вы хотите иметь дело в объекте ListBox  и вернуть этот объект ListBox . Объект  ListBox становится затем объектом виртуального класса, который вы не можете сохранить в переменной. Так что вы должны использовать объект виртуального класса немедленно, вызывая метод или свойство с ним, сохраненный индекс будет использован тоже немедленно.

Этот механизм был разработан так, чтобы пользователь манипулировал временными объектами, без необходимости компилирования для их создания. Это НАСТОЛЬКО быстрее!

Заметьте, что имя виртуального класса должно начинаться с точки. Например, имя виртуального класса, использованного свойством Item - это .ListBoxItem.

Содержание исходного файла класса

Исходный файл класса содержит:

Содержание Header

Структура файла заголовка класса следующая:

  /* MyClass.h */

#ifndef __MYCLASS_H
#define __MYCLASS_H
Включите main.h здесь и любое другое включение необходимое объявлениям, расположенным в этом файле. 
  #include "main.h"
Если класс допускает образцы, объявите структуру ваших объектов. Заметьте, что структура должна начинаться с GB_BASE поля, и что другие поля свободны.
  typedef
struct {
GB_BASE ob;
...
}
MyClassStruct;
main.c будет включать файлы, включающие класс, так что он должен иметь доступ к описанию класса.
  #ifndef __CEXAMPLE_C
extern GB_DESC MyClass[];
extern GB_DESC MyVirtualClassDesc[];
Иначе вы можете объявить полезный макрос, который поможет написать реализацию класса. Например, следующая константа делает код более "читабельным". Заметьте, что она может использоваться только том, где декларирован _object , то есть, внутри реализации метода или свойства. 
  #else
#define THIS ((MyClassStruct *)_object)
#endif

#endif /* __MYCLASS_H */

Содержание Source

Структура исходного файла класса следующая:

  /* MyClass.c */

#define __CEXAMPLE_C
Включите файл заголовка класса, и любые другие включения файлов, нужных содержимому исходного файла (source file).
  #include 
#include
...

#include "MyClass.h"
Если ваш класс возбуждает события, вы должны объявить их с помощью макроса DECLARE_EVENT .
  DECLARE_EVENT(FirstEvent);
DECLARE_EVENT(SecondEvent);
...
Включите любую статическую функцию, которая может понадобиться вашей реализации.
  static void do_job(...)
{
...
}
Затем запишите реализацию каждого метода и свойства.
  BEGIN_METHOD(...)
...
END_METHOD

BEGIN_PROPERTY(...)
...
END_METHOD
И, наконец, последняя часть исходного файла класса - объявление его описания.

Описание класса - это массив структуры GB_DESC , заполненной специальными макросами, объявленными в gambas.h

  GB_DESC MyClassDesc[] =
{
GB_DECLARE("MyClass", sizeof(MyClassStruct)),
...
GB_END_DECLARE
};

Описание класса

Описание класса должно начинаться с GB_DECLARE макроса и заканчиваться макросом GB_END_DECLARE .

Объявление класса

Используйте макрос GB_DECLARE для объявления имени класса и размера его объекта.

Если класс виртуальный, добавьте строку с макросом GB_VIRTUAL_CLASS . Не забудьте, что имя класса должно начинаться с точки, а размер объекта должен быть эквивалентен нулю.

Если класс нормальный, но не создаваемый пользователем, добавьте строку с макросом GB_NOT_CREATABLE .

Затем вы добавите строку объявления каждого символа класса.

Наследование

Ваш класс может наследовать от другого класса, при использовании макроса GB_INHERITS и задании имени родительского класса.

Класс наследует от родителя все методы, свойства, константы и события.

Более того, структура объекта класса должна включать стуктуру родительского объекта в самом начале. Иначе наследование не будет работать!

Пример:

Класс TreeView наследует от Control.

Структура объекта Control следующая:

  typedef
struct {
GB_BASE ob;
QWidget *widget;
...
}
CWIDGET;

А структура объекта TreeView следующая:

  typedef
struct {
CWIDGET widget;
...
}
CTREEVIEW;

Константы

Константа описывается ее именем, ее типом и ее значением.

Для объявления констнаты используйте макрос GB_CONSTANT .

См. описание макроса, где информации больше.

Свойства

Свойства описываются их именем, их типом и их функцией реализации.

Для объявления свойства используйте макрос GB_PROPERTY или макрос GB_STATIC_PROPERTY , если свойство статическое.

Если свойство только для чтения, вы должны использовать вместо этого GB_PROPERTY_READ или GB_STATIC_PROPERTY_READ макрос.

Если вы хотите специальное свойство, которое возвращает тот же объект, что и виртуальный класс, используйте GB_PROPERTY_SELF или GB_STATIC_PROPERTY_SELF макрос.

См. описание макроса, где больше иноформации.

Методы

Метод описывается его именем, его возвращаемым типом, его сигнатурой и его функцией реализации.

Для объявления метода используйте макрос GB_METHOD или макрос GB_STATIC_METHOD , если метод статический.

См. описание макроса, где больше иноформации.

События

Событие описывается его именем, его возвращаемым типом и его сигнатурой.

Для объявления события используйте макрос GB_EVENT . Этот макрос берет указатель переменной как аргумент, чтобы позволить интерпретатору разместить идентификатор для этого события. Эта переменная должна быть объявлена в исходном файле с макросом DECLARE_EVENT .

См. описание макроса, где больше иноформации.

Реализация класса (implementation)

Реализуя метод

Для реализации метода вы должны написать функцию, чей код заключен между двумя макросами: BEGIN_METHOD и END_METHOD.

Макрос BEGIN_METHOD принимает ДВА аргумента: имя функции и список аргументов, разделенных точкой с запятой.

Аргументы метода НЕ отделяются запятыми, поскольку они, в действительности, поля стурктуры, передаваемые функции.

Если ваш метод не принимает аргумент, вы должны использовать BEGIN_METHOD_VOID вместо BEGIN_METHOD. Макрос BEGIN_METHOD_VOID принимает только один аргумент - имя функции.

gambas.h файл включения содержит определения для типов аргумента:

Вы ДОЛЖНЫ использовать эти типы данных!

Для получения параметров у вас есть два макроса : ARG() и VARG().

Макрос ARG() возвращает адрес параметра в стеке интерпретатора и используется с функцией подобной GB.ToZeroString() или GB.Store().

Макрос VARG() возвращает значение параметра.

Пример :

  BEGIN_METHOD ( TheFunctionName , GB_INTEGER anInteger; GB_STRING aString; GB_VARIANT aVariant; GB_BOOLEAN aBoolean; )
Для получения значения параметра вы должны использовать макрос VARG .
    printf("anInteger = %d\n", VARG(anInteger));
Для получения строкового параметра вы должны использовать специальные макросы STRING и LENGTH , чтобы получить адрес строки или ее длины.
    printf("aString = %*.s\n", LENGTH(aString), STRING(aString));
Вы можете также преобразовать строку Gambas в C нуль-завершаемую (zero-terminated) строку с помощью функции GB.ToZeroString . Вы должны использовать макрос ARG для получения адреса параметра, а не макрос VARG , который вернет его значение.
    printf("aString = %.s\n", GB.ToZeroString(ARG(aString)));
GB_VARIANT - это объединение разных типов данных. Поле типа этого объединения - этот одна из констант GB_T_* .
    if (VARG(aVariant).type == GB_T_STRING)
printf("I got the following string: %s\n", VARG(aVariant)._string.value);
GB_BOOLEAN - сохраняется как целое, которое равно нулю, когда FALSE, и отличается от нуля, когда TRUE.
    printf("aBoolean = %s\n", VARG(aBoolean) ? "TRUE" : "FALSE");
Если вы хотите возбуждать ошибку в вашем методе, вы должны использовать функцию GB.Error() для регистрации ошибки и немедленного возвращения после.
  if (VARG(aBoolean))
{
GB.Error("There was an error!");
return;
}
Для возвращения значения из метода вы можете использовать GB.Return() интерфейсные функции: GB.ReturnInteger() для возвращения целого, GB.ReturnBoolean() для возвращения булева значения и т.д.
    GB.ReturnInteger(VARG(anInteger) * 2);

END_METHOD

Реализуя свойство

Для реализации свойства вы должны написать функцию, чей код заключен между двумя макросами : BEGIN_PROPERTY и END_PROPERTY.

Макрос BEGIN_PROPERTY принимает одни аргумент: имя свойства.

Функция вызывается и для чтения, и для записи свойства. Для различения эти дву случаев вы должны использовать макрос READ_PROPERTY . Конечно, если ваше свойство только для чтения, в этом нет нужды.

При чтении свойства вы должны вернуть значение свойства с помощью одной из GB.Return функций.

Написав свойство вы берете значение для записи с помощью макроса VPROP . Этот макрос принимает один аргумент: тип данных свойства, который должнен быть одним из Gambas значений структуры, определенных в gambas.h :

Используйте макрос PROP для получения адреса значения, если вы хотите использовать функции подобные GB.ToZeroString() или GB.Store().

Пример:

  BEGIN_PROPERTY ( ThePropertyName )
Вначале вы должны проверить, хотим ли мы читать или записывать свойство.
    if (READ_PROPERTY)
{
Здесь мы читаем свойство...
      printf("Returning the property value\n");
Макрос THIS определен в файле заголовков класса. Он возвращает указатель на данные структуры объекта.

Здесь мы полагаем, что есть char * AStringProperty , определенное в структуре объекта, что указывает на строку Gambas.

      GB.ReturnString(THIS->AStringProperty);
}
else
{
Здесь мы записываем свойство...

Для сохранения сложного Gambas типа данных, подобно String или Object вы должны использовать GB.StoreString() или GB.StoreObject(). Эти функции имеют дело с подсчетом ссылок. 

      printf("I'm going to write the value: %s\n", GB.ToZeroString(PROP(GB_STRING)));

GB.StoreString(PROP(GB_STRING), &THIS->AStringProperty);
В основном, модифицированное свойство реализует некоторые другие действия...
      printf("Property has been modified. The new value is %s\n", THIS->AStringProperty);
}

END_PROPERTY