Ада-95. Компилятор GNAT

       

Двойная диспетчеризация


Бывают случаи, когда при описании в одном пакете двух различных тэговых типов возникает желание описать подпрограмму которая принимает параметры обоих тэговых типов.

В подобной ситуации получается, что такая подпрограмма будет считаться примитивной операцией для обоих типов.

Однако, компилятор должен иметь какой-нибудь способ определения того, какой параметр использовать для диспетчеризации.

Чтобы, яснее представить суть подобной проблемы, рассмотрим следующий (не корректный!!!) пример

type Message_Type is

tagged record

. . . end record;

type Output_Device is

tagged record

. . . end record;

procedure Put (M : in Message_Type; D : in Output_Device); -- записать сообщение M в устройство вывода D (не допустимо!!!)

. . .



На первый взгляд, идея выглядит достаточно логично, при наличии различных типов устройств вывода иметь процедуру, которая позволяет выдать определенного типа сообщение в устройство вывода определенного типа.

Каждый тип сообщения может переопределить соответствующим образом процедуру вывода Put для того чтобы осуществить свой вывод необходимым образом.

Каждый тип устройства вывода переопределяет процедуру Put

с целью использования средств предоставляемых устройством вывода.

Однако, при вызове процедуры Put становится не понятным какую версию этой процедуры необходимо использовать, ту которая определена в типе, производном от типа Message_Type, или ту которая определена в типе, производном от типа Output_Device.

Ада решает эту проблему очень просто: использование подпрограмм которые являются примитивными операциями для двух (и более) тэговых типов, подобно процедуре Put, - не допускается.

Следовательно, необходимо изменить описание процедуры таким образом, чтобы она была примитивной операцией только для одного типа.

Примером такой модификации может служить следующее:

procedure Put (M : in Message_Type; D : in Output_Device'Class);

Теперь, параметр D больше не имеет тип Output_Device, а значит, процедура Put больше не является примитивной операцией для типа Output_Device.




Однако, внутри процедуры, значение Output_Device'Class

приведет к осуществлению диспетчеризации в случае вызова примитивной операции для типа Output_Device.

Подобный прием называют двойной диспетчеризацией.

Например, предположим, что тип Output_Device имеет следующую примитивную операцию:

procedure Write_Output (D : in Output_Device; S : in String);

В этом случае, тип Message_Child_Type, производный от типа Message_Type, может переопределить реализацию процедуры Put приблизительно следующим образом:

procedure Put (M : in Message_Child_Type; D : in Output_Device'Class) is begin

Put (Message_Type(M), D); -- вызов версии Put предка . . . Write_Output (D, ... ); -- отображение данных сообщения . . . end Put;

Вызов процедуры Put, осуществленный с вовлечением параметра надклассового типа, который соответствует типу Message_Child_Type, и параметром с определенным типом устройства вывода приведет к вызову показанной выше версии процедуры Put.

Это приведет к тому, что отображение данных сообщения будет осуществлено с помощью процедуры Write_Output, реализация которой будет выбрана в результате диспетчеризации.


Содержание раздела