<<< BACK NEXT >>>

СТРУКТУРЫ И ПОРОЖДАЕМЫЕ ТИПЫ

   Структуры в Caper 4 определяются посредством следующей определяющей формы:

   struct <имя структуры> { <описатель члена структуры>
                            [, < описатель члена структуры > . . . ] }
                          [ { [<конструктор>] , [<деструктор>] } ]

<имя структуры> ::= <идентификатор>
<описатель члена структуры> ::= <дескриптор переменной> [<флаг построения>]
<флаг построения> ::= build | ref
build используется для указания компилятору включить тело члена структуры в структуру; данный атрибут имеет смысл только для составных данных – структур, коллекций и массивов.
ref – прямое указание компилятору, что определяемый член структуры будет использован как ссылка. Если атрибут опущен, то член структуры интерпретируется как ссылка.
<конструктор> и <деструктор> - имена-указатели блоков, которые будут вызваны в момент создания и удаления структуры соответственно.

   Так, в приведенном ниже примере

Пример:

struct ListValue { <word> point1, =>
                   <word> point2, =>
                   <block> interp =>
                 }

struct tagStru {                                   =>
                 <int> iVar,                       =>  1
                 <implant ListValue>,              =>  2
                 <great> [20] myNumbers build,     =>  3
                 <ListValue> [10] myFuncs ref,     =>  4
                 <ListValue> [10] otherFuncs,      =>  5
                 <ListValue> list build            =>  6
               }

элемент структуры tagStru с номером 1 (см. нумерацию в комментариях) является переменной простого типа (build не имеет смысла). Элемент с номером 2 фактически развертывается в три члена структуры <ListValue>. Здесь бессмысленны оба атрибута (и build, и ref). Все остальные элементы структуры (3-6) являются составными и могут иметь оба возможных атрибута, что и продемонстрировано. Так, при генерации тела структуры в нем будут размещены тела массива 3 и структуры 6, в то время как под myFuncs и otherFuncs (элементы 4,5) будут выделены места как для обычных переменных, которые будут хранить указания на массивы структур.    Операция указания элемента структуры определяется символом ‘.’ (точка). После определения переменной структуры
 
private <tagStru> stru1, <tagStru> [10] stru2

возможны

   stru1.iVar := 10
   stru1. point1 := stru1.iVar    ;* члены имплантированной структуры
   stru1. point2 := stru1. point1 ;*
   stru1.myNumbers[stru1.iVar ] := 20

Однако

   stru1.myFuncs[1] := user_block

приведет к ошибке выполнения: массив myFuncs не создан, в то время как элемент структуры предназначен для хранения ссылки на массив.

   stru1.list.point1 := 30

будет выполнен корректно: тело структуры ListValue будет встроено в tagStru и определено при построении. Использование implant на первой позиции в определении структуры позволяет выравнивать новую определяемую структуру к определяющей:

   struct tagStru1{ <int> var1, <byte> var2 }
   struct tagStru2{ <implant tagStru1>, =>
                    <float> flVar }
  
   private <tagStru1> stru1, <tagStru2> stru2
   build stru2

   stru1 := stru2  ;* корректное присвоение
   stru1.var1 := 10
   stru1.var2 := ‘A’

однако:
  
   stru2 := stru1  ;* ошибочное присвоение

породит сообщение компилятора об ошибке.К порожденным типам относятся и известные по Caper 3 коллекции. Коллекции являются совокупностями бестиповых переменных.

   collection <имя коллекции> { <переменная коллекции>
                                 [ , <переменная коллекции> . . . ] }
                              [ { [<конструктор>] , [<деструктор>] } ]

<имя коллекции> ::= <идентификатор>
<переменная коллекции> ::= <идентификатор>
<конструктор> и <деструктор> - то же, что и для структур.
Операцией указания элемента коллекции является точка. В то же время, элемент коллекции может быть описан строкой, хранящей символическое имя элемента коллекции, или же порядковым номером элемента в коллекции.

Пример:

collection tagColl { variable1, =>
                     variable2, =>
                     variable3  =>
                   }

private <tagColl> coll_1, <tagColl> coll_2, <string> str, <int> ii := 0

. . .

coll_1.”variable1” := “ABC”
coll_1.”variable2” := 2
coll_1.”variable3” := coll_2

str        := “variable2”
coll_2.str := coll_1.str + 10
coll_1.”variable3” и coll_2 указывают на одну и ту же коллекцию.

coll_1.1 := “CDE”    ;* указание члена коллекции порядковым номером
coll_1.(ii+1) := 10  ;* указание члена коллекции порядковым номером

   Если номер указания элемента превышает количество элементов или меньше 1, то это вызовет ошибку в процессе исполнения программы. То же, если будет указано несуществующее имя элемента коллекции.    Коллекции в программных модулях сопровождаются своими описателями.
Посредством функций виртуальной машины программист может получить данные описания и использовать их в процессе вычислений. Коллекции позволяют более гибкую организацию программирования. Так, если два вида коллекций имеют
элементы с одинаковыми наименованиями (допустим, elem), то

collection tagColl_1 { num1, =>
                       elem, =>
                       num2  =>
                     }

collection tagColl_2 { string1, =>
                       elem, =>
                       string2  =>
                     }

private < tagColl_1> coll_1, < tagColl_2> coll_2, <int> test
. . .
if test
   coll := coll_1
else
   coll := coll_2
endif   
coll.”elem” := 10

   Динамически созданные коллекции могут быть удалены функцией

   DelCollection( <указание коллекции> )

или оператором delete (см. ниже).

Возвращаясь к операторной интерпретации описателя типа отметим, что memnum типа корректен только для порожденных типов и определяет количество элементов структуры:

   var := <memnum ListValue> * 2   ;* удвоенное количество элементов структуры   ;* равно 6-ти.

   var := <memnum tagColl_2>       ;* var == 3

   В Caper 4 особое значение уделено возможностям конструирования данных порожденных типов. Так, расширены возможности задания конструкторов и деструкторов (помимо вышеопределенной формы):

<конструктор> ::= .<идентификатор>
<деструктор>  ::= .<идентификатор>

Данная форма может быть использована только в комплексе с инструкциями

   #use module <имя модуля>
   #use_remove module <имя модуля>
   #use block <имя блока>
   #use struct <имя переменной-структуры>
   #use collect <имя переменной-коллекции>

предворяющими описание структур. Завершение действия #use наступает по

   #nouse

или при встрече новой инструкции #use.Заданные конструкторы и деструкторы являются

  1. при #use module <имя модуля> именами членов коллекции, которая возвращается загруженным и вызванным модулем <имя модуля>;
  2. при #use_remove module - то же самое, что и #use, за исключением того, что загруженный модуль будет автоматически выгружен после использования;
  1. при #use block будет использован определенный в программе блок, при этом компилятор будет учитывать тип возвращаемого блоком значения, согласно которому конструктор и/или деструктор будут указаны

членами структуры (если блок возвращает структуру) или коллекции (если блок возвращает коллекцию);

  1. при #use struct – членами созданной структуры;
  2. при #use collect – членами созданной коллекции.

   Конструкторы и деструкторы могут быть смешанного типа: конструктор образован согласно #use, в то время как деструктор указывать на обычный блок или вовсе отсутствовать, и наоборот.

Примеры:

#use module  sbWindow.obc

struct Window {                                  =>
                             <int>    top,              =>
                             <int>    left,              =>
                             <int>    bottom,        =>
                             <int>    right,            =>
                             <word>   style,         =>                   
                             <word>   cursorID,  =>
                             <string> class,          =>
                             <string> name          =>
                         }  {  .create,  .delete }

#nouse

build private <Window> myWindow( 10, 20, 100, 200 )

   Здесь по buld будет

  1. загружен модуль sbWindow.obc  под именем sbWindow,
  2. вызван блок sbWindow;
  3. под результатом вызова будет пониматься коллекция; будет вызван член коллекции create с параметрами ( 10, 20, 100, 200 ).

Фактически, будет реализовано:
   import sbWindow.obc as sbWindow
   sbWindow().”create”( 10, 20, 100, 200 )

В то время как

   delete myWindow

приведет к

   import sbWindow.obc as sbWindow
   sbWindow().”delete”()

#use module  sbWindow.obc

  struct Window { . . .  }  {  .create,  .delete }

#nouse
   .  .  .
build private <Window> myWindow( 10, 20, 100, 200 )

приведет к
   import sbWindow.obc as sbWindow
   sbWindow().”create”( 10, 20, 100, 200 )
   remove sbWindow

Использование блока выглядит следующим образом:

struct WinMethods{ <block> create,  =>
                                   <block> delete,  =>
                                  . . .
                                }

internal <WinMethods> WinLoader

#use block  WinLoader

struct Window { . . . } { .create, .delete }

#avoid
  flick WinLoader
    import sbWindow.obc as _sbWindow
    return _sbWindow ( MODULE_REMOVE, INTERFACE_STRU )
  endflick
#noavoid

   Отметим еще раз, что WinLoader в данном случае должен быть определен программистом. Всякий раз, когда будет строится экземпляр структуры/коллекции

   build private <Window> myWindow( 10, 20, 100, 200 )

 будет выполняться

   WinLoader().create( 10, 20, 100, 200 )

Для удаления объекта:

   WinLoader().delete()

   Далее,

private <WinMethodsl> winMeth
build  winMeth
winMeth.create := someBlock1
winMeth.delete := someBlock2

#use  struct  winMeth
struct Window { . . . } { .create, .delete }
#nouse

Тогда всякое построение типа

build private  <Window> myWin( 10, 20, 200, 300 )
будет приводить к вызову компоненты create структуры WinMethods:

    winMeth.create( 10, 20, 200, 300 )

Аналогично и для коллекций:
collection WinMethodsColl { create, delete, . . . }

private <WinMethodsColl> winMeth
build  winMeth
winMeth.”create” := someBlock1
winMeth.”delete” := someBlock2

#use  struct  winMeth
struct Window { . . . } { .create, .delete }
#nouse

И тогда -
   build private  <Window> myWin( 10, 20, 200, 300 )
будет приводить к вызову компоненты create коллекции WinMethodsColl:

   winMeth.”create( 10, 20, 200, 300 )

<<< BACK NEXT >>>