Язык idl что это

Язык описания интерфейсов

IDL, или язык описания интерфейсов (англ. Interface Description Language или Interface Definition Language) — язык спецификаций для описания интерфейсов, синтаксически похожий на описание классов в языке C++.

Реализации

Примечания

Смотреть что такое «Язык описания интерфейсов» в других словарях:

Язык программирования — Язык программирования формальная знаковая система, предназначенная для записи компьютерных программ. Язык программирования определяет набор лексических, синтаксических и семантических правил, задающих внешний вид программы и действия,… … Википедия

Язык спецификаций — В этой статье не хватает ссылок на источники информации. Информация должна быть проверяема, иначе она может быть поставлена под сомнение и удалена. Вы можете … Википедия

Язык программирования Java — Java Класс языка: объектно ориентированный, структурный, императивный Появился в: 1995 г. Автор(ы): Sun Microsystems Последняя версия: Java Standard Edition 6 (1.6.14) Т … Википедия

Список языков описания пользовательских интерфейсов — Содержание 1 По производителю или платформе 1.1 Flash 1.2 Java 1.3 Microsof … Википедия

D (язык программирования) — У этого термина существуют и другие значения, см. D. D Семантика: мультипарадигменный: императивное, объектно ориентированное, обобщённое программирование Тип исполнения: компилятор Появился в: 1999 Автор(ы) … Википедия

Паскаль (язык программирования) — Эта статья или раздел нуждается в переработке. В Паскале нет модулей, ООП и прочих новомодных веяний. Описание расширений должно присутствовать только в статьях о соответ … Википедия

Паскаль (язык) — Pascal Семантика: процедурный Тип исполнения: компилятор Появился в: 1970 г. Автор(ы): Никлаус Вирт Паскаль (англ. Pascal) высокоуровневый язык программирования общего назначения. Один из наиболее известных языков программирования, широко… … Википедия

DCOM — (англ. Distributed COM) расширение Component Object Model для поддержки связи между объектами на различных компьютерах по сети. Принцип работы Для создания объекта на удалённой машине, библиотека COM вызывает менеджер управления сервисами… … Википедия

CORBA — (обычно произносится [корба], иногда жарг. [кобра]; англ. Common Object Request Broker Architecture общая архитектура брокера объектных запросов) технологический стандарт написания распределённых приложений, продвигаемый… … Википедия

Apache Thrift — Тип RPC framework Разработчик Apache Software Foundation … Википедия

Источник

Язык idl что это

Дальнейшее применение объектов и компонентов вкупе с внедрением последних в распределенные системы вызвало необходимость создания такого языка программирования, который бы позволил описать любой объект или компонент. И, что не менее важно, это описание должно быть одинаковым для любой платформы. Программисты ответили на эти требования, выдав ‘на-гора’ язык описания интерфейсов IDL (Interface Definition Language). Сегодня за развитием IDL следит международный консорциум OMG, в чьем ведении также находятся технология СОRВА и язык UML.

Вооружившись этими важными понятиями, мы можем приступать к описанию элементов и конструкций языка.

Комментарии

/* Этот текстовый блок будет пропущен компилятором, потому что он закомментирован */

Эти строки будут восприняты компилятором как исходный текст и будут откомпилированы

// а эта строка будет пропущена

Идентификаторы

attribute Object ffx:
attribute Object &_x:
attribute Object 3X;

то компилятор IDL2JAVA из набора Inprise VisiBroker сообщит о том, что найденные символы не совпадают с ожидаемыми. А вот idltojava из JDK 1.2 скажет, что найдена синтаксическая ошибка, и ‘вывалится’ сразу же на первой ошибочной строке.

Компиляторы не различают регистр букв в идентификаторах IDL, следовательно, совершенно разные имена:

attribute string PK;
attribute string pk:

будут восприняты компилятором IDL как одно и то же имя, что приведет к появлению на экране сообщения об ошибке, говорящего о попытке переопределить идентификатор PK. В этом есть свой резон. Если данный фрагмент IDL будет оттранслирован на язык программирования, не различающий регистр букв, то как соответствующий компилятор этого языка сможет различить имена? Поэтому подобная перестраховка не помешает.

Символ подчеркивания ‘_’ не может быть первым в имени идентификатора. Однако очень часто при трансляции IDL, скажем, на язык Java, компилятор сам добавляет перед получаемыми идентификаторами символы подчеркивания, чтобы избежать конфликтов имен, и идентификаторы, начинающиеся с подчеркивания, могут породить проблемы. Так, компилятор idl2java из Visi Broker ‘не видит’ разницы между двумя одинаковыми именами, отличающимися лишь наличием символа подчеркивания вначале, и строки исходного текста:

void getSomethingfin Object param);
void _getSomething(in Object param);

вызовут ошибку переопределения имени getSomething. Реакция других компиляторов может быть иной.

Ключевые слова


Литералы

Булев литерал самый простой. Он применяется при операциях с типом boolean и может принимать значение TRUE или FALSE:

IDL: специальные неотображаемые символы

символ новой строки \n
горизонтальная табуляция \t
вертикальная табуляция \v
backspace \b
carriage return \r
form feed \f
alert \a
обратная косая черта \\
вопросительный знак \?
одинарная кавычка \’
двойная кавычка \»
восьмеричное число \ооо
шестнадцатеричное число \xhh

Набор специальных не отображаемых на экране символов вполне достаточен для повседневной работы, тем более что формы \ооо и \xhh позволяют представить любой символ как его номер в таблице символов в восьмеричном и шестнадцатеричном формате. Обычно компилятор IDL во время трансляции конвертирует подобные шестнадцатеричные коды в символ.

Отдельно нужно сказать о ‘широких’ символьных литералах. Когда одного байта для хранения символа не хватает, применяются так называемые ‘широкие’ символы, содержащие более 8 бит. Таблицы этих символов могут быть разными на разных платформах. Поэтому, задавая литералы с ‘широкими’ символами, следует не выходить за рамки таблицы ISO Latin-1 (8859-1).

Целочисленные литералы могут быть десятичными, восьмеричными и шестнадцатеричными. Десятичные литералы состоят из последовательности цифр, но если последовательность цифр начинается с нуля, то компилятор считает, что перед ним литерал в восьмеричной системе счисления. Шестнадцатеричные литералы могут содержать все цифры и латинские символы от А до F. Подобные литералы должны начинаться с пары символов 0х или 0Х.

Литералы с плавающей точкой могут состоять из целого числа и дробной части, разделенных символом точки (.). Их также можно записать числом в научном формате, т. е. отделить мантиссу числа от его порядка буквой е или Е:

Для финансовых вычислений в IDL предусмотрены литералы с фиксированной точкой, состоящие из целой и дробной частей, разделенных десятичной точкой и отмеченных буквой d или D. Такого рода литералы будут в дальнейшем применяться вместе с типом fixed. Однако, несмотря на то что эти элементы языка описаны в спецификации CORBA V2.2, найти их реализацию в компиляторах IDL не удалось. По крайней мере ни VisiBroker for Java 3.3, ни утилита idltojava из JDK 1.2 не умеют работать с фиксированной точкой.

Препроцессинг

В препроцессинге IDL присутствуют специальные разновидности директивы #pragma:

#pragma ID
#pragma prefix
#pragma version
#pragma hint

Однако они имеют смысл лишь для знающих CORBA, поэтому мы не станем их касаться.

Область видимости имен

К примеру, чтобы сослаться на имя Inner, описанное следующим IDL-файлом:

interface Upper
<
typedef string Inner;
>

При таком стиле создания сложных полных имен компилятору явно указывается, как найти описание соответствующего имени. Заодно снимается потенциальный конфликт имен, возможный при использовании одинаковых имен внутри разных блоков. К примеру, в одном файле IDL могут быть описаны блоки, внутри которых определены одинаковые имена:

interface One
<
typedef float nameContiict;
>;
interface Other
<
typedef float nameConflict;
>;

В этом случае потребуется детализация имен при обращении к ним:

Простые типы

Булев тип. Тип boolean отвечает за хранение логических значений ‘истина’ и ‘ложь’ или, согласно правилам IDL, TRUE и FALSE.

Символьные типы могут описывать обычные 8-битовые символьные данные (тип char) или ‘широкие’ символьные данные (тип wchar), размер которых более 8 бит. В процессе передачи символьных/данных по сети они могут быть переконвертированы так, что изменится их представление, но значение символа будет сохранено. Такая ситуация может возникнуть при передаче данных между компьютерами с отличающимися кодировками.

IDL: диапазоны допустимых хранимых значений для целочисленных типов

Обратите внимание, что тип int в IDL отсутствует.

Для того чтобы описать поля, в которых могут храниться значения любого типа, позволенного IDL, any можно сравнить с универсальным типом void* в языках Си и Си++ или с классом Java.lang.Object в языке Java. Тип any часто применяется в CORBA.

Константы

Источник

Опыт написания IDL для embedded

Предисловие

Я при работе с микроконтроллерами часто сталкивался с бинарными протоколами. Особенно, когда есть несколько контроллеров. Или же используется bluetooth low energy и необходимо написать код для обработки бинарных данных в характеристике. Помимо кода всегда требуется понятная документация.

1. Что такое IDL

Определение IDL довольно простое и уже представлено на wikipedia

IDL, или язык описания интерфейсов (англ. Interface Description Language или Interface Definition Language) — язык спецификаций для описания интерфейсов, синтаксически похожий на описание классов в языке C++.

2. Мотивация

Swagger больше подходит к REST API. Поэтому сразу был отметён. Однако его можно использовать если применяется cbor (бинарный аналог json с кучей крутых фич).

В QFace давно не было коммитов, хотелось некоторых «наворотов» для применения в embedded, возникли сложности при написании шаблона. Он не ищет символы сам, не умеет считать поля enum-ов.

Бесплатные решения было найти сложно, чтобы можно было комфортно использовать при разработке бинарных протоколов.

Поэтому я отказался от генераторов кода и IDL в пользу написания некоторых «автоматизаций» в коде, позволяющих проще писать адаптер протокола. Но протокол с коллегами продолжили описывать при помощи QFace. Решил в свободное время попробовать сделать что-то более или менее годное.

2.1 Обзор QFace

IDL, которая являлась источником вдохновения, имеет простой синтаксис:

Для генерации используется jinja2. Пример:

Концепция интересная. Можно было просто «подпилить» для комфорта «напильником», что конечно и сделал мой коллега. Но мне показалось интересным взять библиотеку sly и просто написать IDL с нужными фичами.

3. Обзор sly

Сначала надо написать лексер. Он токенизирует код чтобы далее было проще обрабатывать парсером. Код из документации:

Также парсер задается очень простым способом (пример из документации):

Каждый метод класса отвечает за парсинг конкретной конструкции. В декораторе @_ указывается правило, которое обрабатывается. Имя метода sly распознает как название правила.

В этом примере сразу происходят вычисления.

Подробнее можно прочитать в официальной документации: https://sly.readthedocs.io/en/latest/sly.html

4. Процесс создания

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

Вначале определили, что модуль состоит из списка термов:

Затем определим, что терм состоит из определений структуры, энумератора или интерфейса разделенные символом «;»( SEPARATOR ):

Здесь терм сразу паковался в массив для удобства. Чтобы список термов ( term term правило) работал уже сразу с листами и собрал в один лист.

Ниже представлен набор правил для описания структуры:

тип ( type_def ), имя ( NAME ), разделитель ( SEPARATOR )

список декораторов ( decorator_item ), тип, имя, разделитель

Это было сделано чтобы поддерживалась следующая конструкция:

Была добавлена возможность python-подобных импортов. Чтобы можно было импортировать из другого модуля только конкретный символ. Это полезно для генерации документации.

Пример реализации метода solve для структуры:

Заключение

Ознакомиться подробнее с реализацией можно по ссылке: https://gitlab.com/volodyaleo/volk-idl

В данном проекте я не обращал особого внимания на скорость работы. Некоторые вещи делал чтобы «быстрее решить задачу». Было важнее получить рабочий код, который можно уже пробовать применять к разным проектам.

Источник

Глава 2. Интерфейсы

В предыдущей главе было показано несколько приемов программирования на C++, позволяющих разрабатывать двоичные компоненты повторного использования, которые со временем могут быть модернизированы. По своему смыслу эти приемы идентичны тем, которые используются моделью СОМ. Незначительные различия между методиками предыдущей главы и теми, которые используются СОМ, в большинстве случаев заключаются в деталях и почти всегда достаточно обоснованы. Вообще-то предыдущая глава прослеживала историю модели СОМ, которая прежде всего и в основном есть отделение интерфейса от реализации.

Снова об интерфейсах и реализациях

Снова об интерфейсах и реализациях

Цель отделения интерфейса от реализации заключалась в сокрытии от клиента всех деталей внутренней работы объекта. Этот фундаментальный принцип предусматривал уровень косвенности, или изоляции (level of indirection), который позволял изменяться количеству или порядку элементов данных в реализации класса без перекомпиляции клиента. Кроме того, этот принцип позволял клиентам обнаруживать расширенную функциональность путем опроса объекта на этапе выполнения. И, наконец, этот принцип позволяет сделать библиотеку DLL независимой от транслятора C++, который используется клиентом.

Хотя этот последний аспект и полезен, он далеко не достаточен для обеспечения универсальной основы для двоичных компонентов. Важно отметить, что хотя клиенты могут использовать любой выбранный ими транслятор C++, в конечном счете это будет всего лишь транслятор C++. Приемы, описанные в предыдущей главе, обеспечивают независимость от транслятора. В конце концов, главное, что необходимо для создания действительно универсальной основы для двоичных компонентов, – это независимость от языка. А чтобы достичь независимости от языка, принцип отделения интерфейса от реализации должен быть применен еще раз.

Рассмотрим определения интерфейса, использованные в предыдущей главе. Каждое определение интерфейса принимало форму определения абстрактного базового класса C++ в заголовочном файле C++. Тот факт, что определение интерфейса постоянно находится в файле, читаемом только на одном языке, вскрывает один остаточный признак реализации этого объекта – язык, на котором он был написан. Но, в конечном счете, объект должен быть доступен для любого языка, а не только для того, который выбрал разработчик объекта. Предусматривая только совместимое с C++ определение интерфейса, разработчик объекта тем самым вынуждает всех использующих этот объект также работать на C++.

Хотя C++ – чрезвычайно полезный язык программирования, существует множество областей программирования, где больше подходят другие языки. Но точно так же, как проблемы совместимости при компоновке можно решить путем обеспечения всех существующих компиляторов файлами определения модуля, возможно и перевести определение интерфейса с C++ на любые другие языки программирования. А так как двоичная сигнатура интерфейса есть просто сочетание vptr/vtbl, этот перевод может быть сделан для большой группы языков.

Проделывание этих языковых преобразований данных для всех известных интерфейсов потребовало бы огромного количества работы, а главное – невозможно успевать делать это для бурного потока языков программирования, которые индустрия программного обеспечения не устает изобретать чуть ли не каждую декаду. Идеально было бы написать сервисную программу, которая переводила бы определения класса C++ в некую абстрактную промежуточную форму. Из этой промежуточной формы такая программа могла бы преобразовывать данные для любого языка программирования, имеющего соответствующий выходной генератор (back-end generator). По мере того как новые языки приобретают значимость, могли бы добавляться новые выходные генераторы, и все ранее определенные интерфейсы смогли бы тотчас использоваться в совершенно новом контексте.

К сожалению, язык программирования C++ полон неоднозначностей, что делает его малопригодным для преобразования данных на все мыслимые языки. Многие из этих неоднозначностей приводят к неопределенным соотношениям между указателями, памятью и массивами. Это не является проблемой, когда оба объекта: вызывающий (caller) и вызываемый (callee) – скомпилированы на С или на C++, но они не могут быть точно переведены на другие языки без дополнительной квалификации. Поэтому, чтобы устранить зависимость определения интерфейса от языка, используемого в какой-либо конкретной реализации, необходимо для определений интерфейсов использовать один язык, а для определений реализации – другой. Если все участники договорятся о едином языке для определений интерфейсов, то станет возможным определить интерфейс однажды и получать по мере необходимости новые представления реализации на специфических языках. СОМ предусматривает язык, который основан на хорошо известном синтаксисе С, но добавляет возможность при переводе на другие языки корректно устранить неоднозначность любых особенностей языка С. Этот язык называется языком описаний интерфейса (Interface Definition Language – IDL).

СОМ IDL базируется на языке определения интерфейсов основного открытого математического обеспечения удаленного вызова процедур в распределенной вычислительной среде – Open Software Foundation Distributed Computing Environment Remote Procedure Call (OSF DCE RPC). DCE IDL позволяет описывать удаленные вызовы процедур не зависящим от языка способом. Это дает возможность компилятору IDL генерировать код для работы в сети, который прозрачным образом (transparently), то есть незаметно для пользователя, переносит описанные операции на всевозможные сетевые средства сообщения. СОМ IDL просто добавляет некоторые расширения, специфические для СОМ, в DCE IDL для поддержки объектно-ориентированных понятий СОМ (например, наследование, полиморфизм). Не случайно, что когда обращение к объектам СОМ осуществляется через границу контекста выполнения[1] или через границы между машинами, все связи клиент-объект используют MS-RPC (реализация DCE RPC, являющаяся частью Windows NT и Windows 95) как основное средство сообщения.

Язык idl что это. Смотреть фото Язык idl что это. Смотреть картинку Язык idl что это. Картинка про Язык idl что это. Фото Язык idl что это

Эти заголовочные файлы также содержат совместимые с С, основанные на структурах определения (structure-based definitions), которые позволяют С-программам обращаться к интерфейсам, описанным на IDL, или обеспечивать их выполнение. То, что MIDL автоматически генерирует С/С++-заголовочный файл, означает, что ни один из СОМ-интерфейсов не нужно определять на C++ вручную. Исход определений из одной точки исключает возникновение множества несовместимых версий определений интерфейсов, которые со временем могут вызвать асинхронность. MIDL также генерирует исходный код, который позволяет использовать интерфейсы в различных потоках, процессах и машинах. Этот код будет обсуждаться в главе 5. И наконец, MIDL может генерировать двоичный файл, который позволяет другим средам, принимающим СОМ, отображать интерфейсы, определенные в исходном IDL-файле, на другие языки. Этот двоичный файл называется библиотекой типа (type library) и содержит разобранный файл IDL в наиболее эффективной для анализа форме. Библиотеки типа обычно распространяются как часть исполняемого файла реализации и позволяют таким языкам, как Visual Basic, Java, Object Pascal использовать интерфейсы, которые выставляются этой реализацией.

IDL позволяет разработчикам интерфейса работать непосредственно в сфере логики, используя синтаксис С. Но в то же время IDL требует от разработчиков точно определять все те аспекты интерфейса, которые не могут быть воспроизведены непосредственно по их логическому описанию на С, с помощью использования аннотаций, формально называемых атрибутами. Атрибуты IDL легко распознать в основном тексте IDL: разделенные запятыми, они заключены в скобки. Атрибуты всегда предшествуют описанию объекта, к которому они относятся. Например, в следующем IDL– фрагменте

v1enum, helpstring(«This is a color!»)

атрибут v1_enum относится к описанию перечисления (enumeration) COLOR. Этот атрибут информирует компилятор IDL о том, что представление COLOR при передаче значения через сеть должно иметь 32 бита, а не 16, как принято по умолчанию. Атрибут helpstring также относится к СОLОR и добавляет строку «This is a color!» («Это – цвет!») в создаваемую библиотеку типа как описание этого перечисления. Если игнорировать атрибуты в IDL-файле, то его синтаксис такой же, как в С. IDL поддерживает структуры, объединения, массивы, перечисления, а также определения типа (typedef) – с синтаксисом, идентичным их аналогам в С.

Определяя методы СОМ в IDL, необходимо четко указать, кто – вызывающий или вызываемый объект – будет записывать или читать каждый параметр метода. Это выполняется с помощью атрибутов параметра [in] и [out]:

void Method1([in] long arg1, [out] long *parg2, [in, out] long *parg3);

Для этого фрагмента IDL предполагается, что вызывающий объект передаст значение в объект arg1 и по адресу, содержащемуся в указателе parg3. По завершении возвращаемые значения будут получены вызывающим объектом по адресам, указанным в parg2 и parg3. Это означает, что для последовательности вызовов:

long arg2 = 20, arg3 = 30;

объект не может полагаться на получение передаваемого значения 20 через parg2. Если объект запускается в том же контексте выполнения, что и вызывающий объект, и оба участника вызова реализованы на C++, то *parg2 действительно будет иметь на входе метода значение 20. Однако если объект вызывается из другого контекста выполнения или один из участников вызова реализован на языке, который сводит на нет оптимизацию начальных значений чисто выходных (out-only) параметров, то инициализация параметра вызывающим объектом будет утеряна.

Методы и их результаты

Результаты методов – это одна из сторон СОМ, где логический и физический миры расходятся. В сущности, все методы СОМ физически возвращают номер ошибки с типом НRESULT. Использование одного типа возвращаемого результата позволяет удаленной COM-архитектуре перегружать результат выполнения метода, а также сообщать об ошибках соединения, просто зарезервировав ряд величин для RPC-ошибок. Величины НRESULT представляют собой 32-битные целые числа, которые передают в вызывающий контекст выполнения информацию о типе ошибок, которые могут произойти (например, ошибки сети, сбои сервера). Во многих языках, поддерживающих СОМ (например, Visual Basic, Java), HRESULT–значения перехватываются контекстом выполнения или виртуальной машиной и преобразуются в программные исключения (programmatic exceptions).

Язык idl что это. Смотреть фото Язык idl что это. Смотреть картинку Язык idl что это. Картинка про Язык idl что это. Фото Язык idl что это

#define SUCCEEDED(hr) (long(hr) >= 0) #def1ne FAILED(hr) (long(hr) _ _

Например, HRESULT с именем STG_S_CONVERTED показывает, что кодом устройства является FACILITY_STORAGE. Это означает, что результат относится к структурированному хранилищу (Structured Storage) или к персистентности (Persistence). Код серьезности ошибки – SEVERITY_SUCCESS. Это означает, что вызов смог успешно выполнить операцию. Третья составляющая – CONVERTED – означает, что в данном случае было произведено преобразование базового файла для поддержки структурированного хранилища. HRESULT-значения, являющиеся универсальными и не привязанными к определенной технологии, используют FACILITY_NULL, и их символическое имя не содержит префикса кода устройства. Вот некоторые стандартные имена HRESULT-значений с кодом FACILITY_NULL:

S_OK – успешная нормальная операция

S_FALSE – используется для возвращения логического false в случае успеха

E_FAIL – общий сбой E_NOTIMPL – метод не реализован

E_UNEXPECTED – метод вызван в неподходящее время

FACILITY_ITF используется в специфически интерфейсных HRESULT-значениях и является в то же время единственным допустимым кодом устройства для HRESULT, определяемых пользователем. При этом значения FACILITY_ITF должны быть уникальными в контексте каждого отдельного интерфейса. Стандартные заголовки определяют макрос MAKE_HRESULT для определения пользовательского HRESULT из трех необходимых полей:

const HRESULT CALC_E_IAMHOSED = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0х200 + 15);

HRESULT Method2([in] short arg1, [out, retval] short *parg2);

на языке Java это соответствует:

public short Method2(short arg1);

в то время как Visual Basic дает такое описание метода:

Function Method2(arg1 as Integer) As Integer

Поскольку C++ не использует поддержку контекста выполнения для обращения к СОМ-интерфейсам, представление этого метода в Microsoft C++ имеет вид:

virtual HRESULT stdcall Method2(short arg1, short *parg2) = 0;

Это значит, что следующий клиентский код на языке C++:

HRESULT hr = pItf->Method2(20, &s);

if (FAILED(hr)) throw hr;

примерно эквивалентен такому Java-коду:

short sum == 10; short s = Itf.Method2(20); sum += s;

Если HRESULT, возвращенный методом, сообщает об аварийном результате, то Java Virtual Machine преобразует HRESULT в исключение Java. Во фрагменте кода на языке C++ необходимо проверить вручную HRESULT, возвращенный этим методом, и соответствующим образом обработать этот аварийный результат.

Интерфейсы и IDL

Определения методов в IDL являются просто аннотированными аналогами С-функций. Определения интерфейсов в IDL требуют расширения по сравнению с С, так как С не имеет встроенной поддержки этого понятия. Определение интерфейса в IDL начинается с ключевого слова interface. Это определение состоит их четырех частей: имя интерфейса, базовое имя интерфейса, тело интерфейса и атрибуты интерфейса. Тело интерфейса представляет собой просто набор определений методов и операторов определения типов:

interface IThisInterface : IBaseInterface

Каждый интерфейс СОМ должен иметь как минимум два атрибута IDL. Атрибут [object] служит признаком того, что данный интерфейс является СОМ-, а не DCE-интерфейсом. Второй обязательный атрибут указывает на физическое имя интерфейса (в предшествующем IDL-фрагменте IThisInterface является логическим именем интерфейса).

Чтобы понять, почему СОМ-интерфейсы требуют физическое имя, отличное от логического имени интерфейса, рассмотрим следующую ситуацию. Два разработчика независимо друг от друга решили создать интерфейс, моделирующий ручной калькулятор. Два их определения интерфейса будут, вероятно, похожими, будучи заданными в общей проблемной области, но скорее всего фактический порядок определений методов и, возможно, сигнатур методов могут в чем-то различаться. Несмотря на это, оба разработчика, вероятно, выберут одно и то же логическое имя: ICalculator.

Клиентская программа на машине какого-нибудь конечного пользователя может реализовать определение интерфейса от первого разработчика, а запустить объект, созданный вторым. Поскольку оба интерфейса имеют одно и то же логическое имя, то если клиент запросит объект для поддержки ICalculator, просто использовав строку «ICalculator», объект ответит на запрос возвратом ненулевого указателя интерфейса. Однако представление клиента о том, на что похож ICalculator, вступит в конфликт с тем, какое представление о нем имеет этот объект, и результирующий указатель будет не тем, которого ожидает клиент. Ведь эти два интерфейса могут быть совершенно разными, несмотря на то, что оба используют одно и то же логическое имя.

Чтобы исключить коллизию имен, всем СОМ-интерфейсам на этапе проектирования назначается уникальное двоичное имя, которое является физическим именем интерфейса. Эти физические имена называются глобально уникальными идентификаторами (Globally Unique Identifiers – GUIDs), что рифмуется со словом squids [1]. GUID используются в СОМ повсюду для именования статических сущностей, таких как интерфейсы или реализации. GUID являются чрезвычайно большими 128-битными числами, что гарантирует их уникальность как во времени, так и в пространстве. GUID в СОМ основаны на универсальных уникальных идентификаторах (Universally Unique Identifiers – UUIDs), используемых в DCE RPC. При использовании GUID для именования СОМ-интерфейсов их часто называют идентификаторами интерфейса (Interface IDs – IIDs). Реализации в СОМ также именуются с помощью GUID, и в этом случае GUID называются идентификаторами класса (Class IDs – CLSIDs ). Будучи представленными в текстовой форме, GUID всегда имеют следующий канонический вид: BDA4A270-A1BA-11d0-8C2C-0080C73925BA

Эти 32 шестнадцатеричные цифры представляют 128-битное значение GUID. Именование интерфейсов и реализации с помощью GUID важно для предотвращения коллизий между разными компонентами.

Для создания нового GUID в СОМ имеется API-функция, которая использует децентрализованный алгоритм уникальности для генерирования нового 128-битного числа, которое никогда больше не встретится в природе:

HRESULT CoCreateGuid(GUID *pguid);

Алгоритм, задействованный в функции CoCreateGuid, использует локальный сетевой интерфейсный адрес машины, текущее машинное время и два постоянных счетчика для компенсации точности часов и нестандартных изменении в них (таких, как переход на летнее время или ручная коррекция системных часов). Если данная машина не имеет сетевого интерфейса, то синтезируется статистически уникальная величина и CoCreateGuid возвращает особого вида HRESULT, показывающий, что данная величина является глобально уникальной только статистически и может считаться таковой только при использовании на локальной машине. Хотя прямой вызов функции CoCreateGuid иногда полезен, большинство разработчиков вызывают ее в неявной форме, применяя из SDK программу GUIDGEN.EXE. На рис. 2.3 показана работа GUIDGEN. GUIDGEN вызывает CoCreateGuid и преобразует полученный GUID в один из четырех форматов, удобных для включения в исходный код на C++ или IDL. При работе в IDL используется четвертый формат (каноническая текстовая форма).

Язык idl что это. Смотреть фото Язык idl что это. Смотреть картинку Язык idl что это. Картинка про Язык idl что это. Фото Язык idl что это

GUID: [object, uuid(BDA4A270-A1BA-11dO-8C2C-0080C73925BA)]

interface ICalculator : IBaseInterface

HRESULT Add([in] long n);

HRESULT Sum([out, retval] long *pn);

При использовании при программировании на С или C++ физического имени интерфейса IID данного интерфейса представляет собой просто логическое имя интерфейса, предшествуемое префиксом IID_. Например, интерфейс ICalculator будет иметь IID, которым можно программно манипулировать, используя сгенерированную IDL константу IID_ICalculator. Для предотвращения коллизий между символическими именами интерфейсов можно использовать пространство имен C++.

Поскольку лишь немногие из компиляторов C++ могут поддерживать 128-битные числа, СОМ определяет С-структуру для представления 128-битовой величины GUID и предлагает псевдонимы для типов IID и CLSID с использованием следующего определения типов:

typedef struct GUID

typedef GUID CLSID;

Внутренняя структура GUID для большинства программистов несущественна, так как единственная значимая операция, которую можно выполнить с GUID, – это проверка их эквивалентности. Для обеспечения эффективной передачи величин GUID как аргументов функций СОМ предусматривает также постоянные псевдонимы для ссылок (constant reference aliases) для каждого типа GUID:

#define REFGUID const GUID&

#define REFIID const IID&

#define REFCLSID const CLSID&

Чтобы иметь возможность сравнивать величины GUID, СОМ обеспечивает функции эквивалентности и перегружает операторы == и != для постоянных ссылок GUID:

inline BOOL IsEqualGUID(REFGUID r1, REFGUID r2)

#define IsEqualCLSID(r1, r2) IsEqualGUID((r1), (r2))

inline BOOL operator == (REFGUID r1, REFGUID r2)

Фактические заголовки SDK содержат условно компилируемые совместимые с С версии определений типа, макросов и встраиваемых функций, как показано выше.

Поскольку показано, что представления имен интерфейсов на этапе выполнения являются GUID, а не строками; это означает, что метод Dynamic_Cast, описанный в предыдущей главе, следует пересмотреть. Действительно, весь интерфейс IЕхtensibleObject должен быть изменен и преобразован в свой аналог IUnknown, совместимый с СОМ.

Интерфейс IUnknown

СОМ-интерфейс IUnknown имеет то же назначение, что и интерфейс IExtensibleObject, определенный в предыдущей главе. Последняя версия IExtensibleObject, появившаяся в конце предыдущей главы, имеет вид:

virtual void *Dynamic_Cast(const char* pszType) = 0;

virtual void DuplicatePointer(void) = 0;

virtual void DestroyPointer(void) = 0;

Для определения типа на этапе выполнения был применен метод Dynamic_Cast, аналогичный оператору C++ dynamic_cast. Для извещения объекта о том, что указатель интерфейса дублировался, использовался метод DuplicatePointer. Для сообщения объекту, что указатель интерфейса уничтожен и все используемые им ресурсы могут быть освобождены, был применен метод DestroyPointer. Вот как выглядит определение IUnknown на C++:

extern «С» const IID IID_IUnknown: interface IUnknown

virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) = 0;

virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;

virtual ULONG STDMETHODCALLTYPE Release(void) = 0;

Заголовочные файлы SDK дают псевдоним interface ключевому слову C++ struct, используя препроцессор С. Поскольку интерфейсы в СОМ определены не как классы, а как структуры, то для того, чтобы сделать методы интерфейса общедоступными, ключевое слово public не требуется. Чтобы создать для целевой платформы СОМ-совместимые стековые фреймы, необходим макрос STDMETHODCALLTYPE. Если целевыми являются платформы Win32, то при использовании компилятора Microsoft C++ этот макрос раскрывается в _stdcall.

IUnknown функционально эквивалентен IExtensibleObject. Метод QueryInterface используется для динамического определения типа и аналогичен С++-оператору dynamic_cast. Метод AddRef используется для сообщения объекту, что указатель интерфейса дублирован. Метод Release используется для сообщения объекту, что указатель интерфейса уничтожен и все ресурсы, которые объект поддерживал от имени клиента, могут быть отключены. Главное различие между IUnknown и интерфейсом, определенным в предыдущей главе, заключается в том, что IUnknown использует идентификаторы GUID, а не строки для идентификации типов интерфейса на этапе выполнения.

IDL-определение IUnknown можно найти в файле unknwn.idl из директории SDK, содержащей заголовочные файлы:

// unknwn.idl – system IDL file

// unknwn.idl – системный файл IDL

[ local, object, uuid (00000000-0000-0000-C000-000000000046) ] interface IUnknown

HRESULT QueryInterface([in] REFIID riid, [out] void **ppv);

ULONG AddRef(void); ULONG Release(void);

Атрибут local подавляет генерирование сетевого кода для этого интерфейса. Этот атрибут необходим для того, чтобы смягчить требования СОМ о том, что все методы при вызове с удаленных машин должны возвращать HRESULT. Как будет показано в следующих главах, интерфейс IUnknown трактуется особым образом при работе с удаленными объектами. Заметим, что фактические, то есть использующиеся на практике IDL-описания интерфейсов, которые содержатся в заголовках SDK, немного отличаются от определений, данных в этой книге. Фактические определения часто содержат дополнительные атрибуты для оптимизации генерируемого сетевого кода, которые не имеют отношения к нашему обсуждению. В случае сомнений обратитесь за полными определениями к последней версии заголовочных файлов SDK.

Интерфейс IUnknown является родительским для всех СОМ-интерфейсов. IUnknown – единственный интерфейс СОМ, который не наследует от другого интерфейса. Любой другой допустимый интерфейс СОМ должен быть прямым потомком IUnknown или какого-нибудь другого допустимого интерфейса СОМ, который, в свою очередь, должен сам наследовать или прямо от IUnknown, или от какого-нибудь другого допустимого интерфейса СОМ. Это означает, что на двоичном уровне все интерфейсы СОМ являются указателями на таблицы vtbl, которые начинаются с трех точек входа: QueryInterface, AddRef и Release. Все специфические для интерфейсов методы будут иметь точки входа в vtbl, которые появляются после этих трех общих точек входа.

Чтобы наследовать от интерфейса IDL, нужно или определить базовый интерфейс в том же IDL-файле, или использовать директиву import, чтобы сделать внешнее IDL-определение базового интерфейса явным в данной области действия:

interface ICalculator : IUnknown

// bring in def. of IUnknown

// импортируем определение IUnknown

HRESULT Add([in] long n);

HRESULT Sum([out, retval] long *pn);

Оператор import может появляться или внутри определения интерфейса, как показано здесь, или предшествовать описанию интерфейса в глобальной области действия. В любом из этих случаев действия оператора import одинаковы, он может многократно импортировать один IDL-файл без всякого ущерба. Поскольку сгенерированный C/C++ заголовочный файл будет требовать С/С++-версии импортируемого IDL-файла, чтобы обеспечить наследование, оператор import из IDL-файла будет странслирован в команду #include в генерируемом заголовочном С/С++-файле:

// calculator.h – generated by MIDL

// calculator.h – генерированный MIDL

// bring in def. of IUnknown

// вводим определения IUnknown

extern «C» const IID IID_ICalculator;

interface ICalculator : public IUnknown

virtual HRESULT STDMETHODCALLTYPE Clear(void) = 0;

virtual HRESULT STDMETHODCALLTYPE Add(long n) = 0;

virtual HRESULT STDMETHODCALLTYPE Sum(long *pn) = 0;

Компилятор MIDL также создаст С-файл, содержащий фактические определения всех GUID, имеющихся в исходном IDL-файле:

// calculator_i.с – generated by MIDL

const IID IID_ICalculator =

0x00, 0х80, 0хC7, 0х39, 0x25, 0xBA > >;

Каждый проект, который будет использовать этот интерфейс, должен или добавить calculator_i.c к своему файлу сборки (makefile), или включить calculator_i.c в один из исходных файлов на С или C++ с использованием препроцессора С. Если это не сделано, то идентификатору IID_ICalculator не будет выделено памяти для его 128-битного значения и проект не будет скомпонован по причине неразрешенных внешних идентификаторов.

СОМ не накладывает никаких ограничений на глубину иерархии интерфейсов при условии, что конечным базовым интерфейсом является IUnknown. Нижеследующий IDL является вполне допустимым и корректным для СОМ:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *