 | Уровень сложности: средний Сандип Кохли, старший инженер-программист, IBM Шрирупа Сен, старший инженер-программист, IBM
18.12.2007 UML - это язык общего назначения. IBM® Rational® Software Architect и IBM® Rational® Systems Developer позволяют расширить функциональность UML-моделирования за счет поддержки конструкций, специфических для языка программирования
C++. Преобразования UML - C++ в Rational Software Architect и Rational Systems Developer конвертируют UML-модели в код на языке C++. В этой статье рассматриваются различные советы по поводу того, как добиться более детализированного контроля над кодом C++, генерируемым при запуске преобразования UML - C++. Статья делится на несколько разделов, в каждом разделе рассматривается одна из методик. Вы можете просматривать эти разделы в любом порядке.
Применение профиля преобразования C++
к моделированию конструкций C++
Обычно для того, чтобы перекинуть мост между унифицированным языком моделирования (Unified Modeling Language, UML) и конкретной предметной областью, используют профили UML. Профиль UML позволяет определить дополнительные семантические правила и характеристики для существующих UML
-элементов - классов, операций и т. п.. Для моделирования элементов, специфичных для C++, то есть структур, объединений, пространств имен (и т. д.), необходимо использовать профиль UML Модуль преобразования UML - C++ в Rational
Software Architect и Rational Systems Developer поставляется в комплекте с профилем преобразования C++. Этот профиль можно применить к UML-модели следующим образом.
- Выберите в обозревателе проектов Project Explorer UML-модель, к которой нужно применить профиль;
- Не снимая выделения с модели в Project Explorer, переключитесь на представление Properties и перейдите на вкладку Profiles;
- Нажмите кнопку Add Profile, как показано на рисунке 1;
Рисунок 1. Применение профиля преобразования UML - C++
- Выберите пункт C++ Transformation из раскрывающегося списка Deployed Profile, показанного на рисунке 2.
Рисунок 2. Выбор
Преобразования C++
Импорт библиотеки типов C++
UML предоставляет ограниченный набор встроенных типов. Это такие типы, как Boolean, Integer, String и
UnlimitedNatural. Большинство языков программирования, в том числе C++, предлагают гораздо более богатый набор примитивов. Если вы моделируете для C++, вам часто будут нужны встроенные элементарные типы, специфичные для C++ (например, при присваивании типа атрибуту, параметру, типу возврата операции и т. д.). Чтобы импортировать библиотеку моделей C++, поставляемую с модулем преобразования C++ Transform, нажмите правой кнопкой мыши на UML-модели в Project Explorer и выберите команду Import Model Library, как показано на рисунке 3.
Рисунок 3. Импорт библиотеки типов C++
Выделите пункт C++ Types в списке Deployed Library, как показано на рисунке 4.
Рисунок 4. Импорт
библиотеки типов C++
Создание пространств имен C++ в модели
В этом разделе рассказывается о том, как моделировать пространства имен C++ в UML-модели. Чтобы создать пространство имен в C++, необходимо применить стереотип cpp_namespace
к UML-пакету. По умолчанию, преобразование UML - C++ отображает UML-пакет в папку. Чтобы отобразить UML-пакет не в папку, а в пространство имен, необходимо применить к нему стереотип cpp_namespace, а затем присвоить свойству NamespaceName, ассоциируемому с этим стереотипом, значение - имя пространства имен. Все классы, структуры, перечисления (и т. п.) стереотипного UML-пакета в формируемом коде будут сгенерированы в этом пространстве имен.
Вам может быть интересно, почему это пространство имен не получает имя стереотипного пакета. Причина - поддержка моделирования анонимных пространств имен в C++. То есть, если вы оставите свойство NamespaceName незаполненным, то пространство имен будет считаться анонимным.
Моделирование определяемых типов C++
Чтобы смоделировать определяемый тип C++(typedef), создайте UML-класс и примените к нему стереотип cpp_typdef. Этот стереотип предлагает три пары свойство/значение:
- arrayDimensions
- ImplementationType
- qualifier
Чтобы создать определяемый тип typedef int const IntMatrix100_20_t [10][20];, создайте UML-класс IntMatrix100_20_t и примените к нему стереотип cpp_typedef. Задайте для этого стереотипа свойства в соответствии с рисунком 5.
Рисунок 5. Создание определяемого типа C++
Чтобы понять зависимость определения определяемого типа от свойств, предоставляемых профилем, вы можете представить себе определяемый тип следующим образом: typedef
<ImplementationType> <qualifier> <Class
Name> <arrayDimensions>
Создание атрибутов многомерных массивов
В этом разделе рассказывается о том, как создать атрибут-трехмерный массив размерностью [10][20][30]. Выберите в обозревателе проектов Project Explorer атрибут, который нужно реализовать в виде многомерного массива. В представлении Properties перейдите на вкладку Stereotypes. На вкладке Stereotypes нажмите кнопку Apply Stereotypes и выберите стереотип cpp_type. Этот стереотип предлагает следующие пары свойство/значение:
- arrayDimensions
- InitializerKind
- isAuto
- isMutable
- isRegister
- isVolatile
- qualifier
В поле arrayDimensions Value, изображенном на рисунке 6, укажите [10][20][30]. В результате этого в исходном коде будет сгенерирован атрибут с размерами массива [10][20][30].
Рисунок 6. Настройка многомерных атрибутов
Определение формального параметра метода как константного
Этот прием использует те же принципы, что и предыдущий (определение многомерного массива). Выделите в Project Explorer параметр, который должен быть константным. В представлении Properties перейдите на вкладку Stereotypes. На вкладке Stereotypes нажмите кнопку Apply Stereotypes и выберите стереотип cpp_type. Этот стереотип предоставляет следующие пары свойство/значение:
- arrayDimensions
- InitializerKind
- isAuto
- isMutable
- isRegister
- isVolatile
- qualifier
Все эти свойства могут быть полезными, но сейчас нас интересует только свойство qualifier. В поле Value для свойства qualifier введите значение const (как показано на Рисунке 5). Благодаря этому
после выполнения преобразования в исходном коде будет сгенерирована сигнатура функции с константным спецификатором для выбранного параметра.
Примечание: убедитесь, что значение, которое вы указываете в этом поле, является корректным. Некорректное значение вызовет ошибку компиляции, которую придется исправлять до выполнения преобразования UML - C++.
Обратите внимание на то, что стереотип cpp_type применим также к атрибутам и классам. Однако для того, чтобы сгенерировать константный атрибут, проще просто пометить его как Read Only.
Как сделать константным весь метод
Предположим, что вы хотите объявить метод константным, чтобы он был сгенерирован с константным ключевым словом, как в
)int Operation1(MyType Parameter1 const;. Для этого необходимо установить флажок для спецификатора Query на вкладке General в представлении Properties для этого метода, как показано на рисунке 7. Чтобы воспользоваться этой возможностью,
нет необходимости в применении стереотипа.
Рисунок 7. Создание константного метода
Добавление исключения в метод
Исключения - это приоритетные элементы в UML, поэтому их можно моделировать без использования профилей. Для упрощения и обобщения практики моделирования преобразования UML - C++ везде, где это возможно, используют свойства UML, а не
профили. Чтобы сгенерировать предложение throw для функции, например, int Operation1() throw ( MyType);, необходимо сначала создать параметр для этой операции и установить для свойства Is Exception значение true.
Имя параметра не имеет значения, поскольку оно игнорируется в процессе преобразования, но лучше выбрать такое имя,
которое позволит правильно идентифицировать исключение при его возникновении. На рисунке 8 показано, как установить для параметра Is Exception значение true.
Рисунок 8. Установление типа вызова исключительной ситуации для метода
Контроль над предложениями include в генерируемом коде
Преобразование UML - C++ предназначено для автоматического выделения отношений из модели с последующим генерированием корректных предложений include или объявлений forward.
Однако могут возникнуть ситуации, в которых вы захотите контролировать формирование предложений include. Например, вы хотите использовать в теле метода локальные переменные определенного типа, и поэтому вам нужно, чтобы в файле тела были сгенерированы предложения include для этого типа.
Вам придется явным образом смоделировать такие случаи, создав UML-отношение между этими двумя классами и применив к этому отношению стереотип cpp_dependency. Этот стереотип поставляется со свойством IsInclusionInHeader, значение которого
по умолчанию равно false. Если вы хотите, чтобы предложения include были сгенерированы в файле тела,
то вы должны оставить значение по умолчанию (false). Если эти предложения include нужно сгенерировать в заголовке, то следует либо вообще не применять стереотип cpp_dependency к созданному отношению, либо применить стереотип и установить для свойства IsInclusionInHeader значение true.
Использование секций сохранения кода при воспроизведении преобразования на уровне файла
Если вам нужно использовать в исходном коде типы из стандартной библиотеки или из какой-либо другой библиотеки, то в модели такие строки необходимо определить как простые строки (plain string). Например, если нужно объявить атрибут как vector of integers, в модели вы определили бы его тип как vector<int>. В процессе преобразования этот тип будет считаться примитивным типом, в результате чего для него не будут сгенерированы предложения
include или объявления forward. Следовательно, такие типы необходимо включить в исходные файлы явным образом.
Например, для типа vector необходимо вставить в исходный код явное предложение include для типа vector: #include <vector>. Для поддержки этой возможности в каждом сгенерированном файле предусмотрена секция, которая будет сохранена дословно при повторном применении преобразования UML-C++, как показано в следующем листинге (см. листинг 1). Обратите внимание на строку //TODO: Add definitions that you want preserved (Добавьте определения, которые должны быть сохранены). Все, что вы напишете между комментариями Начало секции и Завершение секции, будет сохранено. Именно сюда можно добавлять такие предложения, как #include <vector>.
Листинг 1. Код, сгенерированный для класса Car
#ifndef CLASS1_H
#define CLASS1_H
//Начало секции для файла Class1.h
//TODO: Добавьте определения, которые должны быть сохранены
//Завершение секции для файла Class1.h
#include "MyType.h"
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
class Class1
{
//Начало секции для Class1
//TODO: Добавьте определения, которые должны быть сохранены
//Конец секции для Class1
public:
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
int Operation1()const throw ( MyType);
}; //Завершение описания класса Class1
#endif
|
Удаление секции повторного применения преобразования на уровне класса
Обратите внимание на то, что в коде предыдущего листинга имеется строка //Начало секции для Class1 ... //Завершение секции для Class1 . Вы можете поместить здесь все специфические предложения языка C++, которые вы не можете смоделировать, и они будут сохраняться до тех пор, пока вы явным образом их не удалите. При повторном применении преобразования UML - C++ предложения в этой секции не будут перезаписаны. Если вам не нужна эта секция в генерируемом коде, то, возможно, вы захотите удалить ее. При повторном выполнении преобразования UML - C++ она не будет сгенерирована снова. Именно так вы сможете избавиться от комментариев, которые вам не нужны.
В этом случае удалите следующую секцию (см. листинг 2), после чего она не будет восстановлена автоматически. Если вам снова понадобится эта секция, то придется вставить ее вручную в этом же месте кода.
Листинг 2. Код, сгенерированный для класса Car
...
//Начало секции для Class1
//TODO: Добавьте определения, которые должны быть сохранены
//Завершение секции для Class1
...
|
Как изменить имя генерируемого пакета, не изменяя UML-модель
Чтобы изменить имя пакета, выполните двойной щелчок на конфигурационном файле преобразования, чтобы открыть его в редакторе. Перейдите на вкладку Mapping и установите флажок Enable mapping, как показано на рисунке 9.
Рисунок 9. Разрешение отображения модели
Нажмите кнопку New, чтобы создать модель отображения по умолчанию и дать ей подходящее имя. Нажмите кнопку Edit Mapping, чтобы вывести на экран диалоговое окно, показанное на рисунке 10.
Рисунок 10. Редактирование модели отображения
Предположим, вы работаете с UML-моделью, показанной на рисунке 11. Если у вас нет модели отображения, то класс Date будет сгенерирован в папке с именем Package1. Если же вы хотите,
чтобы Package1 был сгенерирован как Folder1, тогда вам нужно будет использовать модель отображения. Для этого в модели отображения перейдите к элементу Package1, а затем в редактируемом поле Mapped Name в нижней части страницы мастера введите имя Folder1. Теперь в генерируемом коде класс
Date будет помещен в папку с именем Folder1.
Рисунок 11. Рабочая UML-модель
Как сгенерировать несколько классов в одном файле
Когда мы отображаем класс UML в модели отображения на другое имя, мы изменяем имя файла, в котором он будет сгенерирован, но не имя класса, который будет сгенерирован в этом файле. По умолчанию высокоуровневый класс генерируется в файле, имя которого соответствует
имени этого класса. Значит, для класса UML с именем MyClass преобразование UML - C++ сгенерирует файлы MyClass.h и MyClass.cpp.
Однако вы можете выбрать другое имя для UML-класса при помощи модели отображения. Аналогично тому, как мы переименовали UML-пакет в модели отображения, чтобы сгенерировать другое имя для папки, можно отобразить UML-класс в файл с другим именем. Имя самого класса модель отображения не меняет. Следовательно, чтобы сгенерировать несколько классов в одном файле,
достаточно дать им всем одно и то же целевое имя в модели отображения.
Ресурсы Научиться
Получить продукты и технологии
Обсудить
Об авторах  | |  | Сандип Кохли (Sandeep Kohli) - ведущий разработчик и архитектор в рабочей группе Rational Software Architect / Rational Systems Developer в г. Бангалор. Он работал с различными инструментами моделирования Rational, включая Rational Rose, Rational RoseRT и Rational Software Architect и, кроме того, с компиляторами C/C++/Fortran/Ada. |
 | |  | Шрирупа Сен (Sreerupa Sen) работает в IBM в качестве разработчика архитектуры и имеет дело с инструментами UML-моделирования Rational. На протяжении своей карьеры в сфере разработки программного обеспечения ей приходилось работать в различных областях, в том числе она занималась банковскими приложениями, связующим ПО, центрами вспомогательных данных и инструментами моделирования. В IBM она занималась продуктами Rational Software Architect/Rational Systems Developer, особенно связанными с языком C++. |
Выскажите мнение об этой странице
|  |