 | Level: Intermediate Sandeep Kohli (sandeep.kohli@in.ibm.com), Senior Software Engineer,
IBM
Sreerupa Sen (sreerupa.sen@in.ibm.com), Senior Software Engineer,
IBM
12 Jun 2007 UML is a general-purpose language. IBM® Rational® Software Architect and IBM® Rational® Systems Developer allow you to extend UML modeling to support constructs that are specific to the C++ programming
language. The UML to C++ Transformation in Rational Software Architect and Rational Systems Developer converts UML models to C++. This article discusses various tips on how to achieve finer control over the C++ code generated when you run the UML to C++ transformation. This article is divided into several sections, with each section describing a technique. You don't need to refer to the sections in any particular order.
Applying the C++
Transformation profile for modeling C++ constructs
The standard way to bridge a gap between the Unified Modeling Language (UML) and a particular domain is through the use of a UML Profile. A UML profile allows you to define additional semantics and characteristics for existing UML
elements, such as class, operations, and so on. In order to model C++ specific elements like struct, unions, namespaces (and so on), you need a UML profile. The UML to C++ Transformation in Rational
Software Architect and Rational Systems Developer comes with a C++ Transformation profile. The profile can be applied on a UML model as follows.
- Select the UML model on which the profile is to be applied in the Project Explorer.
- Switch to the Properties View with the model still selected in the Project Explorer and click the Profiles tab.
- Click the Add Profile button, as shown in Figure 1.
Figure1. Applying the UML to C++ Transformation profile
- Select C++ Transformation from the Deployed Profile drop down list, as shown in Figure 2.
Figure 2. Selecting
C++ Transformation
Importing the C++ type library
UML provides a limited set of predefined types. These are Boolean, Integer, String and
UnlimitedNatural.. Most programming languages, including C++, provide a much richer set of primitives. When modeling in the C++ domain, you will often need to use a predefined primitive type specific to C++ (for example, when you assign a type to an attribute, a parameter, an operation return type, and so on). To import the C++ model library that is shipped with the C++ Transform, right-click the UML model in the Project Explorer and select Import Model Library, as shown in Figure 3 below.
Figure 3. Importing the C++ Type Library
Select C++ Types from the Deployed Library list, as shown in Figure 4.
Figure 4. Importing
the C++ Type Library.
Creating C++ namespaces in a model
This section tells you how to model C++ namespaces in a UML model. To create a namespace in C++, you need to apply the cpp_namespace
stereotype on a UML package. A UML package will, by default, be mapped to a folder by the UML to C++ transformation. To get a UML package to map to a namespace instead of a folder, you need to apply the cpp_namespace stereotype on it, and then set the NamespaceName property associated with this stereotype to the desired namespace name. All the classes, struct, enums (and so on) under this stereotyped UML package will be generated within this namespace in the generated code.
You might wonder why the namespace does not take the name of the stereotyped package as its own name. The reason is to support modeling anonymous namespaces in C++. So, if you leave the NamespaceName property blank, the namespace is assumed to be an anonymous namespace.
Modeling C++ typedefs
To model a C++ typedef, create a UML class and apply the cpp_typdef stereotype on it. This stereotype provides three property/value pairs:
- arrayDimensions
- ImplementationType
- qualifier
To create a typedef such as typedef int const IntMatrix100_20_t [10][20];, create a UML class IntMatrix100_20_t and apply the cpp_typedef stereotype to it. Set the properties for this stereotype to be like those shown in Figure 5.
Figure 5. Creating a C++ typedef
To correlate the typedef definition with the properties provided by the profile, you can think of the typedef in the following manner: typedef
<ImplementationType> <qualifier> <Class
Name> <arrayDimensions>
Creating multi-dimensional array attributes
In this section, you will learn how to make an attribute a three-dimensional array of size [10][20][30]. Select the attribute that needs to be a multi-dimensional array in the Project Explorer. In the Properties view, click the Stereotypes tab. On the Stereotypes tab click the Apply Stereotypes button and select the cpp_type stereotype. This stereotype provides you with the following property/value pairs:
- arrayDimensions
- InitializerKind
- isAuto
- isMutable
- isRegister
- isVolatile
- qualifier
In the arrayDimensions Value field shown in Figure 6, specify [10][20][30]. This will generate an attribute with array dimensions [10][20][30] in the source code.
Figure 6. Setting Multidimensional Attributes
Qualifying a method's formal parameter as const
This trick applies the same principles as the last one (defining a multi-dimensional array). Select the method parameter that you want to be a const in the Project Explorer. In the Properties view, click the Stereotypes tab. On the Stereotypes tab, click the Apply Stereotypes button and select the cpp_type stereotype. This stereotype provides you with the following propery/value pairs.
- arrayDimensions
- InitializerKind
- isAuto
- isMutable
- isRegister
- isVolatile
- qualifier
Each one of these is an interesting property, but for now you are interested in the qualifier property. In the Value field for qualifier, enter the value const (as shown in Figure 5 previously). This will generate a function signature in the source code with the const qualifier for the selected parameter,
once the transformation is executed.
Note: Make sure to enter a valid value in this field. Failure to do so will lead to compilation errors that you will need to fix before rerunning the UML to C++ Transformation.
Note that the cpp_type stereotype is also applicable to attributes of a class. However, it is easier to simply mark an attribute as Read Only in order to generate a const attribute.
Making an entire method as a const method
Suppose you would like to declare your method as const so that it is generated with the const keyword as in
)int Operation1(MyType Parameter1 const;. To do so, you need to select the Query qualifier on the General tab in the Properties view for the method in question, as shown in Figure 7. There is no need
to apply a stereotype to get this capability.
Figure 7. Creating a const method
Adding an exception to a method
Exceptions are first class citizens in UML, and can be modeled without using profiles. In order to simplify and generalize the modeling experience, the UML to C++ Transformation uses UML properties wherever applicable, instead of
using a profile. To generate a throw clause for a function, such as int Operation1() throw ( MyType);, you first need to create a parameter for the operation and set its Is Exception property to true.
The name of the parameter doesn't matter, because it is ignored during transformation, but it is better to select a name
that properly identifies it as an exception being thrown. Figure 8 shows how to set a parameter's Is Exception property to true.
Figure 8. Setting the throw type for a method
Controlling include statements in the generated code
The UML to C++ Transformation is designed to pick up dependencies from the model on its own, and to generate proper include statements or forward declarations automatically.
However, you may want to control include statement generation for specific situations. For example, you may know that you will use local variables of a certain type in your method body, and therefore want to generate include statements for that type in your body file.
You need to model such cases explicitly by creating a UML dependency between the two classes and applying the cpp_dependency stereotype to this dependency. This stereotype comes with a property named IsInclusionInHeader, which
is set to false by default. If you want the include statement to be generated in the body file,
then you should leave it as false. If you need to generate the include statement in the header, then either do not apply the cpp_dependency stereotype to your dependency at all, or apply the stereotype and set the IsInclusionInHeader property to true.
Using File level reapply preserve sections
If you need to use types from the standard library, or from some other library in your source code, you have to set such types as plain strings in your model. For example, if you want to declare an attribute to be a vector of integers, you would specify its type as vector<int> in the model. The transform would treat such types as primitive types, and would not
generate any include statements or forward declarations corresponding to them. Therefore, you need to include these in your source files explicitly.
For the vector type, for example, you should put an explicit include statement for vector in your source code, such as #include <vector>. To support this, each generated file contains a section that will be preserved verbatim when you reapply the UML to C++ transformation, as shown in Listing 1 following. Note the //TODO: Add definitions that you want preserved line. Whatever you add here between the Begin section and End section comments will be preserved. Please add statements like #include <vector> here.
Listing 1. Generated Code for a class Car
#ifndef CLASS1_H
#define CLASS1_H
//Begin section for file Class1.h
//TODO: Add definitions that you want preserved
//End section for file Class1.h
#include "MyType.h"
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
class Class1
{
//Begin section for Class1
//TODO: Add attributes that you want preserved
//End section for Class1
public:
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
int Operation1()const throw ( MyType);
}; //end Class Class1
#endif
|
Removing the class level reapply preserve sections
Notice that in the code from Listing 1 previously, there is a //Begin section for Class1 ... //End section for Class1 . You can put all those C++ specific statements that you cannot model here, and these will be preserved until you delete them explicitly. When you rerun the UML to C++ Transformation, it will not overwrite statements in this section. In case you do not need this section in your generated code, you may want to delete it altogether. It will not be regenerated next time you run the UML to C++ Transformation. This is how you can get rid of those comments that you don't care about.
Delete the following section (Listing 2) if you don't need it, and it will not be regenerated. Should you want this section back, you will have to insert it manually at the same location later.
Listing 2. Generated Code for a class Car
...
//Begin section for Class1
//TODO: Add attributes that you want preserved
//End section for Class1
...
|
Changing the name of the generated package without modifying the UML model
To change the package name, double click your transformation configuration file to open it in the editor. Click the Mapping tab and select Enable mapping as shown in Figure 9.
Figure 9. Enable the mapping model
Click the New button to create a default mapping model and give it an appropriate name. Click the Edit Mapping button to bring up the dialog box shown in Figure 10.
Figure 10. Edit mapping model
Suppose you are working on the UML model shown in Figure 11. If you do not have a mapping model, then the Date class will be generated inside a folder called Package1. If, however, you want
the Package1 to be generated as Folder1, then you need to use the mapping model. To do so, browse the mapping model to reach Package1, and then in the Mapped Name edit box at the bottom of the wizard type Folder1. In the generated code, the
Date class will now be generated inside a folder named Folder1.
Figure 11. The UML model on which you are working
Generating multiple classes in the same file
If you map a UML class in the mapping model to a different name, you change the name of the file in which it will be generated but not the generated class in the file. By default, a top-level class is generated into a file with the same name as
itself. So for a UML class named MyClass, the UML to C++ Transformation will generate MyClass.h and MyClass.cpp.
However, you can choose to have a different filename for the UML class by using the mapping model. Just like you can rename a UML package in the mapping model to generate a differently named folder, you can map a UML class to a differently named file. The name of the class itself is not affected by the mapping model. To generate multiple classes in the same file,
therefore, all you need to do is to give the same target name for them in the mapping model.
Resources Learn
- For technical resources, visit the
developerWorks
Rational Systems Developer
area. You'll find technical documentation, how-to articles, education, downloads,
product information, and more.
- Stay current with
Technical events and webcasts.
- How to use the C/C++ Development Toolkit (CDT) : C/C++ development with the Eclipse Platform.
- In the
Architecture area on developerWorks,
get the resources you need to advance your skills in the architecture arena.
- In the
Patterns area on developerWorks,
get the resources you need to advance your skills in the patterns arena.
- Enroll in
RD101:
Principles of Modeling with UML 2.0. This online course introduces the basic principles of object technology and visual
modeling using the Unified Modeling Language (UML), version 2.0. You will learn the four principles of visual modeling and the basic building blocks of the UML, as well as the benefits of the use case-driven, architecture-centric process that the UML supports. This is self-directed, self-paced online learning with rich media content, including interactions, quizzes, and virtual labs .
- Browse the
technology bookstore
for books on these and other technical topics.
Get products and technologies
Discuss
About the authors  | 
|  | Sandeep Kohli is a development lead and Architect with the Rational Software Architect / Rational Systems Developer team in Bangalore. He has worked on various Rational modeling tools like Rational Rose, Rational RoseRT, and Rational Software Architect. He has also worked on C/C++/Fortran/Ada compilers. |
 | 
|  | Sreerupa Sen is an architect with IBM working on Rational UML modeling tools. In the course of her career in software, she has worked on different domains, including banking applications, middleware, utility data centers, and modeling tools. In IBM, she has been working with IBM's Rational Software Architect/Rational Systems Developer offerings, specifically using C++. |
Rate this page
|  |