Реализация интерфейсов в GNU Ada и C++

Объект представлен в памяти некоторой структурой данных. Первый элемент - это указатель на таблицу адресов процедур и функций, реализующих допустимые операции над объектом. Такую таблицу обычно называют таблицей виртуальных функций (ТВФ) или vtable.

При множественном наследовании создается несколько ТВФ. Структура данных объекта содержит по дополнительному указателю на ТВФ для каждого ответвления в дереве наследования интерфейсов.

Ниже рассматривается компилятор GCC версии 4.1.1.


GNU C++

Рассмотрим класс Gamma, наследующий два интерфейса Alpha и Beta.

class Alpha {
public:
    int данные_alpha;
    virtual void методы_alpha ();
};
 
class Beta {
public:
    int данные_beta;
    virtual void методы_beta ();
};
 
class Gamma : public Alpha, public Beta {
public:
    int данные_gamma;
    virtual void методы_gamma ();
};

Полный исходный текст и полученный код на ассемблере приведены на отдельной странице.

Объект типа Gamma имеет структуру:

указатель на ТВФ Alpha+Gamma ← адрес объекта типа Gamma или Alpha
данные Alpha
указатель на ТВФ Beta ← адрес объекта типа Beta
данные Beta
данные Gamma

Для класса Gamma в сегменте кода (readonly) создается таблица-диспетчер:

0 - смещение к началу
указатель на описатель типа
методы Alpha ← ТВФ Alpha+Gamma
методы Beta
методы Gamma
-8 - смещение к началу
указатель на описатель типа
методы-переходники Beta (thunks) ← ТВФ Beta

Методы-переходники (thunks) представляют собой небольшие дополнительные функции, которые корректируют указатель на объект (добавляя смещение к началу) и затем вызывают основные функции-методы.

Описатель типа нужна для конструкций typeid и dynamic_cast. Его можно отключить флагом -fno-rtti.


GNU Ada

В языке Ada95 объектам соответствуют структуры типа tagged record. В версии Ada2005 появились интерфейсы - interface. При множественном наследовании разрешается только один тип tagged record, остальные должны быть интерфейсами.

Хорошая статья на эту тему - "The Implementation of Ada 2005 Interface Types in the GNAT Compiler".

Рассмотрим класс Gamma, наследующий класс Alpha и интерфейс Beta.

type Alpha is abstract tagged record
        data_alpha : Integer;
end record;
procedure func_alpha (x: in out Alpha) is abstract;
 
type Beta is interface;
procedure func_beta (x: in out Beta) is abstract;
 
type Gamma is new Alpha and Beta with record
        data_gamma : Integer;
end record;
procedure func_alpha (x: in out Gamma);
procedure func_beta (x: in out Gamma);
procedure func_gamma (x: in out Gamma);

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

Объект типа Gamma имеет структуру:

указатель на ТВФ Alpha+Gamma ← адрес объекта типа Gamma или Alpha
данные Alpha
указатель на ТВФ Beta(Gamma) ← адрес объекта типа Beta
данные Gamma

Компилятор создает функцию pkg_elabs() - elaboration function. Она инициализирует таблицы-диспетчеры и описатели типов. Ее следует вызывать до первого обращения к функциям данного пакета.

Для типа Gamma таблица-диспетчер pkg_gammaT выглядит так:

00000201h — дескриптор таблицы
0 — смещение к началу
&pkg_gammaB — указатель на описатель типа
&pkg_size_3 — метод Gamma’Size ← ТВФ Gamma
&pkg_alignment_3 — метод Gamma’Alignment
0
0
0
0
&pkg_Oeq_3 — метод Gamma.”=”
&pkg_assign_3 — метод Gamma.”:=”
0
0
0
0
0
0
0
&pkg_func_alpha_2 — метод Gamma.func_alpha
&pkg_func_beta_2 — метод Gamma.func_beta
&pkg_func_gamma — метод Gamma.func_gamma

Для типа Gamma с интерфейсом Beta таблица-диспетчер pkg_T281s выглядит так:

00000301h — дескриптор таблицы
8 — смещение к началу
&pkg_T282s — указатель на описатель типа
&pkg_T539s — переходник Gamma’Size ← ТВФ Beta(Gamma)
&pkg_T548s — переходник Gamma’Alignment
0
0
0
0
&pkg_T557s — переходник Gamma.”=”
&pkg_T566s — переходник Gamma.”:=”
0
0
0
0
0
0
0
&pkg_T578s — переходник Gamma.func_beta

Поскольку типы Alpha и Beta - абстрактные, объекты этих типов не могут быть созданы. Поэтому таблицы-диспетчеры pkg_alphaT и pkg_betaT в процессе выполнения программы не используются.


Совместимость между C++ и Ada

Для Ada компилятор порождает 15 дополнительных методов. Два из них - Size() и Alignment() - можно использовать и в C++. Остальные не имеют смысла для интерфейсов и могут быть пустыми.

Для получения в C++ класса, совместимого по интерфейсу с Ada, предлагается в качестве базового класса использовать Ada_Compatible:

class Ada_Compatible {
public:
	virtual int Size () = 0;
	virtual int Alignment () = 0;
	virtual void _TSS_Stream_Read () = 0;
	virtual void _TSS_Stream_Write () = 0;
	virtual void *_TSS_Stream_Input () = 0;
	virtual void _TSS_Stream_Output () = 0;
	virtual int _Op_Eq () = 0;
	virtual void _Assign () = 0;
	virtual void _TSS_Deep_Adjust () = 0;
	virtual void _TSS_Deep_Finalize () = 0;
	virtual void _Disp_Asynchronous_Select () = 0;
	virtual void _Disp_Conditional_Select () = 0;
	virtual void _Disp_Get_Prim_Op_Kind () = 0;
	virtual void _Disp_Get_Task_Id () = 0;
	virtual void _Disp_Timed_Select () = 0;
 
	inline void* Tag () { return *(void**) this; }
};
 
#define ADA_COMPATIBLE_IMPLEMENTATION \
	virtual int Size () { return sizeof (*this) * 8; } \
	virtual int Alignment () { return sizeof (void*); } \
	virtual void _TSS_Stream_Read () {} \
	virtual void _TSS_Stream_Write () {} \
	virtual void *_TSS_Stream_Input () { return 0; } \
	virtual void _TSS_Stream_Output () {} \
	virtual int _Op_Eq () { return 0; } \
	virtual void _Assign () {} \
	virtual void _TSS_Deep_Adjust () {} \
	virtual void _TSS_Deep_Finalize () {} \
	virtual void _Disp_Asynchronous_Select () {} \
	virtual void _Disp_Conditional_Select () {} \
	virtual void _Disp_Get_Prim_Op_Kind () {} \
	virtual void _Disp_Get_Task_Id () {} \
	virtual void _Disp_Timed_Select () {}

Пример реализации класса Arithmetic с интерфейсом Sequence:

#include "ada-compatible.h"
 
class Sequence : public Ada_Compatible {
public:
	virtual int Value () = 0;
	virtual void Next () = 0;
};
 
class Arithmetic : public Sequence {
	ADA_COMPATIBLE_IMPLEMENTATION;
 
	int val;
public:
	virtual int Value ()
	{
		return val;
	}
	virtual void Next ()
	{
		val += 1;
	}
};

Интерфейс Sequence соответствует следующему коду на языке Ada:

type Sequence is interface;
function Value (x: in Sequence) return Integer is abstract;
procedure Next (x: in out Sequence) is abstract;

Преобразование и сравнение типов

Для динамического преобразования и сравнения типов необходимо реализовать дополнительные методы, например:

virtual char *Type_Name () = 0;
virtual void *Cast (char *type_name) = 0;
function Type_Name (x: in Тип) return Interfaces.C.char_array is abstract;
function Cast (x: in Тип; type_name: Interfaces.C.char_array) return System.Address is abstract;

 
proj/lang/gcc-interfaces.txt · Последние изменения: 2006/06/14 08:50 vak
 
Copyright (C) 1996-2013 Serge Vakulenko
serge@vak.ru