Prototyping an application
Managing projects and targets
Implementing an application
Documenting an application
Automatic guessing
Generating accessors
Variants
UML extensions
This section is a detailed tutorial indicating how to customize the C++ Designer generator. The goal of this example is to handle stereotyped classes to generate specific code.
The customization happens over five stages:
Creating a variant
To create a new variant, you only have to copy the structure of the standard variant directory, found in your project’s resources.
The “standard” variant directory
The “init.py” file is mandatory in the main variant’s directory, as well as in the “acts” directory, in order to make python act available to the engine. If this file is missing, your python ACT won’t be loaded.
The description file must be updated to indicate what your custom variant does.
Name the copied variant “CustomVariant”. For now, remove all content from the “acts” and “types” directories, and open the “products” directory.
Defining a new product
Our new stereotype is available on Classes, that is to say we have to edit the “Class.py” product file.
All we have to do is add a new generation task when an element has the <<CxxCustomClass>> stereotype.
1# Product definition for Class
2# Bound variables:
3# PRODUCT the product being build
4# ELT the element to generate
5
6import act
7from java.util import ArrayList
8from com.modeliosoft.modelio.api.mda.model import ObUtils
9
10if (CXX.isCxxElement(ELT) and not act.isNoCode(ELT)):
11 # headerFile = CXX.makeNamespacePath(ELT) + "/" + CXX.makeCxxName(ELT) + "." + CXX.makeHeaderFileExtension(ELT)
12 headerFile = CXX.makeDefaultHeaderFilePath(ELT)
13
14 # bodyFile = CXX.makeNamespacePath(ELT) + "/" + CXX.makeCxxName(ELT) + "." + CXX.makeBodyFileExtension(ELT)
15 bodyFile = CXX.makeDefaultBodyFilePath(ELT)
16
17 files = ArrayList()
18 if (ELT.isStereotyped("CxxCustomClass")):
19 PRODUCT.addFileGeneration(ELT, files, "CustomClassGen")
20 else:
21 if (act.isExternal(ELT)):
22 if (ObUtils.isTagged (ELT, "Cxx.GenerateHeaderFile")):
23 files.add(headerFile)
24 else:
25 files.add(headerFile)
26 files.add(bodyFile)
27 PRODUCT.addFileGeneration(ELT, files, "standard.ClassGen")
The new product file launches a generation using a new ACT, “CustomClassGen”, when the current class is stereotyped <<CxxCustomClass>>. Otherwise, the standard ACT, “ClassGen” is applied.
Using “standard.ClassGen” means the generator will take the “ClassGen” ACT from the standard variant. It is slightly different from entering only “ClassGen”, which would search an ACT with this name in the current variant before launching the standard one.
We now have to add the new ACT called from this product.
Defining a new python ACT
In the “acts” directory of the custom variant, just add a new file called CustomClassGen.py.
Here is a sample showing the content of this file:
1#
2# Template CustomClassGen
3#
4from com.modeliosoft.modelio.cxxdesigner.engine.act import IAct
5from com.modeliosoft.modelio.api.model import *
6from com.modeliosoft.modelio.api.mda.model import ObUtils
7import act
8
9class CustomClassGen (IAct):
10
11 ################################################################################
12 # Generation code
13 #
14 def run(self, ctx, el):
15 hxx = ctx.getOutputs()[0]
16 cxx = ctx.getOutputs()[1]
17
18 hxx.println("// Write custom code for the header here")
19
20 cxx.println("// Write custom code for the header here")
It is usually a better idea to modify an existing ACT rather than to create a new one from scratch.
The variant will be ready to use as soon as it has been declared in your Modelio project.
Declaring the variant
Open the C++ project’s edition box, and select the generation tab.
The “Generation” tab of the project edition box
We have to select the variant in this tab, but this isn’t available right now.
Open the variant management box, in the upper right corner.
The “Variant management” box
Add a new variant, and select the directory corresponding to “CustomVariant”.
Here it is, the variant is available in your project. Once selected, it will be usable for the C++ generation.
All guessing files must be valid with the following XML schema, corresponding to the “standard/types/ type_guessing.xsd” file.
<?xml version=“1.0” encoding=“utf-8” ?>
<xs:schema attributeFormDefault=“unqualified” elementFormDefault=“qualified” xmlns:xs=“http://www.w3.org/2001/XMLSchema”>
<xs:simpleType name=“pointer-type”>
<xs:restriction base=“xs:string”>
<xs:enumeration value=“” />
<xs:enumeration value=“PTR” />
<xs:enumeration value=“REF” />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name=“cli-pointer-type”>
<xs:restriction base=“xs:string”>
<xs:enumeration value=“” />
<xs:enumeration value=“PTR” />
<xs:enumeration value=“REF” />
<xs:enumeration value=“CLIPTR” />
</xs:restriction>
</xs:simpleType>
<xs:complexType name=“declaration”>
<xs:sequence>
<xs:element minOccurs=“1” maxOccurs=“1” name=“container” type=“xs:boolean” />
<xs:choice minOccurs=“1” maxOccurs=“1”>
<xs:element minOccurs=“0” maxOccurs=“1” name=“pointer” type= “pointer-type” />
<xs:element minOccurs=“0” maxOccurs=“1” name=“cliPointer” type= “cli-pointer-type” />
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name=“typeDef”>
<xs:sequence>
<xs:element minOccurs=“0” name=“OptionalSimple” type=“declaration” />
<xs:element minOccurs=“0” name=“MandatorySimple” type=“declaration” />
<xs:element minOccurs=“0” name=“OptionalMultiple” type=“declaration” />
<xs:element minOccurs=“0” name=“MandatoryMultiple” type=“declaration” />
<xs:element minOccurs=“0” name=“Finite” type=“declaration” />
</xs:sequence>
</xs:complexType>
<xs:complexType name=“types”>
<xs:sequence>
<xs:element minOccurs=“1” maxOccurs=“unbounded” name=“basicType”>
<xs:complexType>
<xs:complexContent mixed=“false”>
<xs:extension base=“typeDef”>
<xs:attribute name=“name” type=“xs:string” use=“optional” />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element minOccurs=“1” maxOccurs=“unbounded” name=“primitiveType”>
<xs:complexType>
<xs:complexContent mixed=“false”>
<xs:extension base=“typeDef”>
<xs:attribute name=“name” type=“xs:string” use=“optional” />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element minOccurs=“1” maxOccurs=“unbounded” name=“classType”>
<xs:complexType>
<xs:complexContent mixed=“false”>
<xs:extension base=“typeDef”>
<xs:attribute name=“name” type=“xs:string” use=“optional” />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:element name=“type-guessing”>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=“1” maxOccurs=“1” name=“attribute” type=“types” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“association-end” type=“types” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“in-parameter” type=“types” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“inout-parameter” type=“types” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“return-parameter” type=“types” />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Variants are used to implement and/or customize advanced code generation from a UML model.
This chapter covers the concept of code generation using variants, the syntax of templates, type libraries and other elements, and common techniques used to make the most of the flexibility and customization capabilities of the C++ Designer engine.
The C++ Designer engine is based on the following concepts:
Active code templates: An active code template (ACT) is a python or java file. It generally follows the text that needs to be generated, involving the navigation through the UML model, extraction of the information from it into the result text, and so on. Active code templates can refer to each other, to allow a better separation of concerns. For information on the syntax of templates and an example of a simple active code template, please see “Template syntax”.
Product definition file: For each particular model element being processed, the product definition corresponding to its metaclass is considered. A typical generation product is a python file whose content associates the element with a particular ACT, and navigates to some other model elements (typically owned by the current element) to repeat the product process recursively. For more information and an example of a product definition file, see “Product file syntax”.
Type definitions: A type definition is an XML document that contains additional meta-information for better translation of UML constructs to respective programming language constructs. A typical example is the translation of a UML type into a programming language type. The type definition mainly contains a type expression template that can be used to declare variables for the type in question, as well as several important meta-attributes that are used during generation to find the best matching representation (type definition) for a UML construct, based on its multiplicity and other more advanced characteristics. For an example of an ACT type definition, please see “Type definition syntax”.
Type library: A type library contains type definition, for example corresponding to a certain general-purpose library that exists for a programming language. For an example of a library definition.
Variant: A variant is a customization unit for the C++ module, containing ACT files, product files or type libraries. A variant must be declared in a C++ project before being utilized.
A variant has a specific directory organization, to make the customization easier. The following image shows the content of the standard variant.
Standard variant content
The “acts” directory groups together all active code templates, the “products” directory contains product definition files, and the “types” directory contains all type libraries.
The “description.txt” file is mandatory, and must contain the variant’s description, which will be displayed at runtime when choosing the current variant for a project.
A variant may redefine only part of the module’s behaviour, as the standard behaviour will be taken into account in other cases.
Note: An empty “__init__.py
” file must be created in a variant in order for python ACTs to work correctly.
A product definition jython file defines behaviour when encountering an element according to its metaclass (represented by the product’s name).
For each particular model element being processed, the product definition associates the element with a particular ACT, and navigates to some other model elements (typically owned by the current element) to repeat the product process recursively.
A file generation task contains the following parameters:
The element you want to generate on.
The relative path of at least one file to generate (usually both the “header” and “body” files).
The ID of a template that must be used to generate the file contents. IDs can be relative (“PackageGen”) or absolute, with a specific variant name (for example, “standard.PackageGen”).
You must use the PRODUCTS.addFileGeneration method.
A subproduct task contains a list of all model elements to process the products on.
The sub-elements must be given in a List as a parameter of the PRODUCTS.addSubElements method.
1# Product definition for Package
2# Bound variables:
3# PRODUCT the product being build
4# ELT the element to generate
5
6import act
7from java.util import ArrayList
8from com.modeliosoft.modelio.api.mda.model import ObUtils
9
10if (CXX.isCxxElement(ELT) and not act.isNoCode(ELT)):
11 # headerFile = CXX.makeNamespacePath(ELT) + "/" + CXX.makeCxxName(ELT) + "." + CXX.makeHeaderFileExtension(ELT)
12 headerFile = CXX.makeDefaultHeaderFilePath(ELT)
13
14 # bodyFile = CXX.makeNamespacePath(ELT) + "/" + CXX.makeCxxName(ELT) + "." + CXX.makeBodyFileExtension(ELT)
15 bodyFile = CXX.makeDefaultBodyFilePath(ELT)
16
17 files = ArrayList()
18
19 if (act.isExternal(ELT)):
20 if (ObUtils.isTagged (ELT, "Cxx.GenerateHeaderFile")):
21 files.add(headerFile)
22 else:
23 files.add(headerFile)
24 files.add(bodyFile)
25
26 PRODUCT.addFileGeneration(ELT, files, "PackageGen")
27
28 subelements = ELT.getOwnedElement()
29
30 PRODUCT.addSubElements(subelements)
An Active Code Template definition consists of a python or java file found in a variant. It generally follows the text that needs to be generated, involving the navigation through the UML model, extraction of the information from it into the result text, and so on. Active code templates can refer to each other, to allow a better separation of concerns.
Let’s take the “AttributeGen.py” file as an example to show how a python ACT works. It is part of the standard variant, found in C++ Designer’s resources.
The start of an ACT
Every ACT file starts with a comment zone like this:
1#
2# Template AttributeGen
3# Binded variables
4# CXX facilities for Cxx specific production and navigation
5# GEN facilities for code production
6# MDL facilities for model navigation and condition testing
7# ENG ACT ENG (used to call another template))
8#
9##############################################################################
10##
11
12# Local utilities functions
13# naming rules:
14# - if the utility function directly prints out the generated code it must be named printXXXXX
15# where XXXX is expected to summarize the function role
16# - if the utility function returns a piece of generated code as a
17
18string it must be named makeXXXXXX
19# where XXXX is expected to summarize the function role
20# - if the utility function is used to get some elements of the model and return a list of model elements (navigation convinience)
21# it must be named modelGetXXXXX where XXXX is expected to summarize the function role
22# other rules:
23# - any utility function takes a model element as first parameter (name it el)
24# - utility function must not define global variables of their own
25#
This comment indicates: * the current template’s name, AttributeGen * the available global variables for all python files, corresponding to service classes from C++ Designer, CXX, GEN, MDL and ENG * several rules about how functions in an ACT are created
The python module’s declaration then begins:
1from com.modeliosoft.modelio.cxxdesigner.engine.act import IAct
2from com.modeliosoft.modelio.api.model import *
3from com.modeliosoft.modelio.api.mda.model import ObUtils
4from java.util import ArrayList
5import act
6
7INDENT = _" "_ # four white spaces
8
9class **AttributeGen** (IAct):
10
A python ACT file must implement the com.modeliosoft.modelio.cxxdesigner.engine.act.IAct interface to become available in the engine. It defines only the main entry point of the ACT, which will be described below.
The run function
When evaluating an ACT file, the C++ Designer’s engine always launches run:
1##############################################################################
2#
3# Generation code
4#
5
6 def run(_self_, ctx, attribute):
7 out = ctx.getOutputs()[0]
8 self.printAttributeDeclaration(out, attribute)
9
Three parameters are given to this function:
The first thing to do in the run function is usually to extract the current output from the execution context.
We then have to analyse the given model element to determine which code must be generated. In this example, it is done in a separate utility function.
Utility functions
1def **makeMemberDeclaration**(_self_, att):
2 # standard case
3 # declaration syntax is build as:
4 # decl = $specifiers $decoratedtype $namespacedname$bindings $init;
5 # decoratedtype = $containerpointers $container($type)
6 # type = $basetype $pointers
7 #
8 # example:
9 # static std::vector<int*> C1::att;
10
11 # compute declaration
12 decoratedtype=GEN.makeHxxSignature(att)
13
14 # compute bindings
15 bindings=""
16 if (ObUtils.isTagged(att, "Cxx.Bind")):
17 bindings = "<"
18 for p in ObUtils.getTagValues(att, "Cxx.Bind"):
19 bindings = bindings + p + ", "
20 bindings = bindings.rstrip(", ")
21 bindings = bindings + ">"
22
23 # final assembly
24 decl = decoratedtype + bindings
25
26 return decl
27
28 def printAttributeDeclaration(self, out, el):
29 if act.hasDocumentation(el):
30 out.println(act.makeDocumentationComment(el))
31 out.print(INDENT + _self_.makeMemberDeclaration(el) + ";")
32
Utility functions are the core of the generation process, as they contain the code generation itself.
The naming rule quoted earlier is applied on those two functions; the first one creates a string from an attribute, whereas the second writes content in the output.
When customizing C++ generation, you only have to modify those functions and include the behaviour you need.
Calling another ACT
Active code templates are often divided into several sub-templates, to allow a better understanding and separation of concerns. The ACT engine, given as a global variable (ENG), allows another template to be run and its result obtained. Let’s take a part of another python file as an example,
ClassGen:
1 def printBodyContent(self, out, el):
2 out.println()
3 self.printUseIncludes(out, el)
4 out.println (_"//class header file"_)
5 out.printf("#include \"%s\"\n", [CXX.makeHeaderFilename(el)])
6 out.println()
7 self.printBodyAutoIncludes(out, el)
8 self.printIncludeNotes(out, el)
9 self.printBodyTopNotes(out, el)
10 self.printOpenNamespace(out, el)
11 out.println(ENG.evalAct("ClassDefinitionCxx", el))
12 self.printCloseNamespace(out, el)
13 self.printBodyBottomNotes(out, el)
14
The printBodyContent method is the main part of the body file generation. Includes and notes are produced by several utility functions in ClassGen, but the core of the class content is generated by the ClassDefinitionCxx ACT.
The ENG.evalAct method executes this ACT on the given element, and returns its result (i.e. everything that had been printed in the output), which is itself printed in the current body file.
An ACT type definition consists of the <type> XML element, which has a number of attributes containing important meta-information about the type, and which contains a <declaration> element defining the type declaration.
An ACT type definition may also contain one or more <include> elements, containing information about #include directives that may be required in order to use this type in generated code.
When Modelio C++ Designer generates code for a UML attribute, parameter or association end, it considers the type definitions available in the current library path and matches them against the name of the UML element designated as being the type of the attribute/parameter/association end being generated, as well as other decorations (for example containers).
You can generate the type of your choice by creating a new type definitions file in a variant.
The following is an example of basic type definition, used when generating a “string” type (used for example by an attribute).
1 <type name="string">
2 <include path="string" />
3 <declaration>std::string</declaration>
4 <decoration>
5 <parameter-passing>REF</parameter-passing>
6 <return-passing>REF</return-passing>
7 </decoration>
8 </type>
With this definition, the generated type will be std::string, and its include also is generated at the beginning of the generated file.
The <decoration> XML element indicates additional C++ decorations that will be added if the type is used in a parameter or a return parameter. Here, a reference is added.
The following is an example of basic type definition, used when generating a “set” container (used for example by an attribute).
1 <type name="set">
2 <include path="set" />
3 <declaration>$containerTypeName<$valueTypeName></declaration>
4 <container name="std::set" unique="true" ordered="true"/>
5 <decoration>
6 <class-storage>PTR</class-storage>
7 </decoration>
8 </type>
Here, the declaration contains some macros. $containerTypeName corresponds to the container name, defined in the next line, and $valueTypeName is the basic type of the element chosen from your model. Let’s assume this is a “string” attribute.
The <decoration> XML element indicates that an additional C++ decoration will be added to the type stored in the container.
With this definition, the generated type will be std::set<* std::string>, and its include is also generated at the beginning of the generated file.
The <container> XML element also contains two attributes, indicating whether or not the container is unique or order. There properties are used in the container mapping.
When choosing a container in automatic decoration mode, several properties of your UML model are used to choose the best matching container.
The container mapping part of a type definition file indicated how this corresponding container is chosen.
Container kind | Has Key | Ordered | Unique |
---|---|---|---|
OrderedMap | X | X | X |
UnorderedMap | X | X | |
OrderedMultiMap | X | X | |
UnorderedMultiMap | X | ||
OrderedSet | X | X | |
UnorederedSet | X | ||
OrderedCollection | X | ||
UnorderedCollection |
Here is an extract of the container mapping part of the STL type library file:
1 <container-mapping>
2 <OrderedMap>
3 <containerref name="map" />
4 </OrderedMap>
5 <UnorderedMap>
6 <containerref name="hash_map" />
7 <containerref name="map" />
8 </UnorderedMap>
9 <OrderedMultiMap>
10 <containerref name="multimap" />
11 </OrderedMultiMap>
12 <UnorderedMultiMap>
13 <containerref name="hash_multimap" />
14 </UnorderedMultiMap>
15 <OrderedSet>
16 <containerref name="set" />
17 </OrderedSet>
18 <UnorderedSet>
19 <containerref name="hash_set" />
20 </UnorderedSet>
21 <OrderedCollection>
22 <!-- No ordered collection defined -->
23 <degradedKind>UnorderedCollection</degradedKind>
24 </OrderedCollection>
25 <UnorderedCollection>
26 <containerref name="vector" />
27 </UnorderedCollection>
28 </container-mapping>
The <containerref> XML element indicates directly which container should be used.
A <degradedKind> XML element indicates that no defined container respects all the properties, but that another one will be used instead.
In the example given above, when looking for an ordered container, it will correspond to the OrderedCollection kind. No direct match is found, but the UnorderedCollection kind is then chosen, leading to the usage of a “vector” container.
All type libraries must be valid with the following XML schema, corresponding to the “standard/types/ type_library.xsd” file.
<?xml version=“1.0” encoding=“utf-8” ?>
<xs:schema attributeFormDefault=“unqualified” elementFormDefault=“qualified” xmlns:xs=“http://www.w3.org/2001/XMLSchema”>
<xs:simpleType name=“passing”>
<xs:restriction base=“xs:string”>
<xs:enumeration value=“VALUE” />
<xs:enumeration value=“REF” />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name=“storage”>
<xs:restriction base=“xs:string”>
<xs:enumeration value=“PTR” />
<xs:enumeration value=“REF” />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name=“cliStorage”>
<xs:restriction base=“xs:string”>
<xs:enumeration value=“PTR” />
<xs:enumeration value=“REF” />
<xs:enumeration value=“CLIPTR” />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name=“containerKind”>
<xs:restriction base=“xs:string”>
<xs:enumeration value=“OrderedMap” />
<xs:enumeration value=“UnorderedMap” />
<xs:enumeration value=“OrderedMultiMap” />
<xs:enumeration value=“UnorderedMultiMap” />
<xs:enumeration value=“OrderedSet” />
<xs:enumeration value=“UnorderedSet” />
<xs:enumeration value=“OrderedCollection” />
<xs:enumeration value=“UnorderedCollection” />
</xs:restriction>
</xs:simpleType>
<xs:complexType name=“containerDeclaration”>
<xs:attribute name=“name” type=“xs:string” use=“required” />
<xs:attribute name=“unique” type=“xs:boolean” use=“required” />
<xs:attribute name=“ordered” type=“xs:boolean” use=“required” />
</xs:complexType>
<xs:complexType name=“containerRef”>
<xs:choice minOccurs=“1” maxOccurs=“unbounded”>
<xs:element minOccurs=“1” maxOccurs=“1” name=“degradedKind” type=“containerKind” />
<xs:element name=“containerref”>
<xs:complexType>
<xs:attribute name=“name” type=“xs:IDREF” />
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
<xs:element name=“type-library”>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=“0” maxOccurs=“unbounded” name=“type”>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=“0” maxOccurs=“1” name=“include”>
<xs:complexType>
<xs:attribute name=“path” type=“xs:string” use=“required” />
</xs:complexType>
</xs:element>
<xs:element minOccurs=“1” maxOccurs=“1” name=“declaration”>
<xs:complexType>
<xs:simpleContent>
<xs:extension base=“xs:string” />
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element minOccurs=“0” maxOccurs=“1” name=“container” type=“containerDeclaration” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“decoration”>
<xs:complexType>
<xs:choice minOccurs=“1” maxOccurs=“1”>
<xs:sequence minOccurs=“1” maxOccurs=“1”>
<xs:element minOccurs=“1” maxOccurs=“1” name= “parameter-passing” type=“passing” />
<xs:element minOccurs=“1” maxOccurs=“1” name= “return-passing” type=“passing” />
</xs:sequence>
<xs:choice minOccurs=“1” maxOccurs=“1”>
<xs:element minOccurs=“0” maxOccurs=“1” name= “class-storage” type=“storage” />
<xs:element minOccurs=“0” maxOccurs=“1” name=“cli- class-storage” type=“cliStorage” />
</xs:choice>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name=“name” type=“xs:ID” use=“required” />
</xs:complexType>
</xs:element>
<xs:element minOccurs=“1” maxOccurs=“1” name=“container- mapping”>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=“1” maxOccurs=“1” name=“OrderedMap” type=“containerRef” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“UnorderedMap” type=“containerRef” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“OrderedMultiMap” type=“containerRef” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“UnorderedMultiMap” type=“containerRef” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“OrderedSet” type=“containerRef” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“UnorderedSet” type=“containerRef” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“OrderedCollection” type=“containerRef” />
<xs:element minOccurs=“1” maxOccurs=“1” name=“UnorderedCollection” type=“containerRef” />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name=“id” type=“xs:string” use=“required” />
</xs:complexType>
</xs:element>
</xs:schema>
Automatic guessing on association ends happens when C++ Designer is generating the code for an association end and when the automatic generation flag is set.
Association ends point to classes, therefore the automatic guessing algorithm will favor pointer declaration of objects and will avoid passing any object by value for performance reasons.
For an association end, the relevant information is as follows:
The following list shows the generated declaration for an association end named “assoc”.
Association end type: MyClass
MyClass* assoc;
MyClass* assoc;
std::vector<MyClass*> assoc;
std::vector<MyClass*> assoc;
std::vector<MyClass*> assoc;
Association end type: MyDatatype isPrimitive=false
MyDatatype* assoc;
MyDatatype* assoc;
std::vector<MyDatatype*> assoc;
std::vector<MyDatatype*> assoc;
std::vector<MyDatatype*> assoc;
Note 1: As an association end is made to associate two complex classes, even primitive types are treated as complex.
Note 2: The std::vector is replaced by:
Automatic guessing on attributes happens when C++ Designer is generating the code for a single attribute for which the automatic generation flag is set.
For attributes, which in most cases are supposed to be primitive types (integer, float, char, boolean), the automatic guessing algorithm will favour declaration by value. However, pointer declaration will be used to deal with the 0 minimum cardinality cases.
For automatic guessing on attributes, the relevant information is as follows:
The following list shows the generated declaration for an attribute named “att”.
0..1
: int* att;
1..1
: int att;
0..*
: std::vector<int> att;
1..*
: std::vector<int> att;
n..m
: std::vector<int> att;
0..1
: std::string* att;
1..1
: std::string att;
0..*
: std::vector<std::string> att;
1..*
: std::vector<std::string> att;
n..m
: std::vector<std::string> att;
0..1
: MyClass* att;
1..1
: MyClass att;
0..*
: std::vector<MyClass*> att;
1..*
: std::vector<MyClass*> att;
n..m
: std::vector<MyClass*> att;
0..1
: MyDatatype* att;
1..1
: MyDatatype att;
0..*
: std::vector<MyDatatype> att;
1..*
: std::vector<MyDatatype> att;
n..m
: std::vector<MyDatatype> att;
0..1
: MyDatatype* att;
1..1
: MyDatatype att;
0..*
: std::vector<MyDatatype*> att;
1..*
: std::vector<MyDatatype*> att;
n..m
: std::vector<MyDatatype*> att;
Note 1: Where the table states integer, this can be any basic type (integer, char, float, boolean).
Note 2: The std::vector is replaced by:
Note 3: An element typed undefined is generated as void*.
These are the accessors generated by C++ Designer when the “Create accessors” command is activated. These accessors are created in the model. They are automatically decorated for C++ generation.
Card | Declaration | Get accessor | Set accessor |
---|---|---|---|
0..1 | MyClass* assoc; | MyClass* getAssoc() const | void setAssoc(MyClass* value) |
1..1 | MyClass* assoc; | MyClass* getAssoc() const | void setAssoc(MyClass* value) |
0..* | std::vector<MyClass*> assoc; | std::vector<MyClass*>& getAssoc() const | |
1..* | std::vector<MyClass*> assoc; | std::vector<MyClass*>& getAssoc() const | |
n..m | std::vector<MyClass*> assoc; | std::vector<MyClass*>& getAssoc() const |
Card | Declaration | Get accessor | Set accessor |
---|---|---|---|
0..1 | MyDatatype* assoc; | MyDatatype* getAssoc() const | void setAssoc(MyDatatype* value) |
1..1 | MyDatatype* assoc; | MyDatatype* getAssoc() const | void setAssoc(MyDatatype* value) |
0..* | std::vector<MyDatatype*> assoc; | std::vector<MyDatatype*>& getAssoc() const | |
1..* | std::vector<MyDatatype*> assoc; | std::vector<MyDatatype*>& getAssoc() const | |
n..m | std::vector<MyDatatype*> assoc; | std::vector<MyDatatype*>& getAssoc() const |
Note: Accessors with maximum cardinality “*” return a reference to the internal collection, so the user can directly manipulate the container contents using the accessors specific to the container type. There are no “set” accessors for these cases and we recommend that you use the “get” accessor and the proper container API on the returned value.
These are the accessors generated by C++ Designer when the “Create accessors” command is activated. These accessors are created in the model, meaning they are created in the form of modeled methods. They are automatically decorated for subsequent C++ generation.
Card | Declaration | Get accessor | Set accessor |
---|---|---|---|
0..1 | int* att; | const int* getAtt() const | void setAtt(int* value) |
1..1 | int att; | int getAtt() const | void setAtt(int value) |
0..* | std::vector<int> att; | std::vector<int>& getAtt() const | |
1..* | std::vector<int> att; | std::vector<int>& getAtt() const | |
n..m | std::vector<int> att; | std::vector<int>& getAtt() const |
Card | Declaration | Get accessor | Set accessor |
---|---|---|---|
0..1 | std::string* att; | std::string* getAtt() const | void setAtt(std::string* value) |
1..1 | std::string att; | std::string getAtt() const | void setAtt(std::string value) |
0..* | std::vector<std::string> att; | std::vector<std::string>& getAtt() const | |
1..* | std::vector<std::string> att; | std::vector<std::string>& getAtt() const | |
n..m | std::vector<std::string> att; | std::vector<std::string>& getAtt() const |
Card | Declaration | Get accessor | Set accessor |
---|---|---|---|
0..1 | MyClass* att; | MyClass* getAtt() const | void setAtt(MyClass* value) |
1..1 | MyClass att; | MyClass& getAtt() const | void setAtt(MyClass value) |
0..* | std::vector<MyClass*> att; | std::vector<MyClass*>& getAtt() const | |
1..* | std::vector<MyClass*> att; | std::vector<MyClass*>& getAtt() const | |
n..m | std::vector<MyClass*> att; | std::vector<MyClass*>& getAtt() const |
Card | Declaration | Get accessor | Set accessor |
---|---|---|---|
0..1 | MyDatatype* att; | MyDatatype* getAtt() const | void setAtt(MyDatatype* value) |
1..1 | MyDatatype att; | MyDatatype getAtt() const | void setAtt(MyDatatype value) |
0..* | std::vector<MyDatatype> att; | std::vector<MyDatatype>& getAtt() const | |
1..* | std::vector<MyDatatype> att; | std::vector<MyDatatype>& getAtt() const | |
n..m | std::vector<MyDatatype> att; | std::vector<MyDatatype>& getAtt() const |
Card | Declaration | Get accessor | Set accessor |
---|---|---|---|
0..1 | MyDatatype* att; | MyDatatype* getAtt() const | void setAtt(MyDatatype* value) |
1..1 | MyDatatype att; | MyDatatype& getAtt() const | void setAtt(MyDatatype value) |
0..* | std::vector<MyDatatype*> att; | std::vector<MyDatatype*>& getAtt() const | |
1..* | std::vector<MyDatatype*> att; | std::vector<MyDatatype*>& getAtt() const | |
n..m | std::vector<MyDatatype*> att; | std::vector<MyDatatype*>& getAtt() const |
Note 1: Where the table states integer, this can be any basic type (integer, char, float, boolean).
Note 2: Accessors with maximum cardinality “*” return a reference to the internal collection, so the user can directly manipulate the container contents using the accessors specific to the container type. There are no “set” accessors for these cases and we recommend that you use the “get” accessor and the proper container API on the returned value.
Automatic guessing on parameters happens when C++ Designer is generating the code for a parameter for which the automatic generation flag is set.
For parameters, the automatic guessing algorithm selects the best way of passing a parameter, based on good practices:
The C++ Designer generator also takes care of the parameter passing mode (In or InOut) in order to further optimize the generated code.
The following list shows the generated declaration for a parameter named “param”. Please note that the use of const values is favored due to the “In” mode.
Cardinality | Declaration |
---|---|
0..1 | int* param |
1..1 | int param |
0..* | const std::vector<int>& param |
1..* | const std::vector<int>& param |
n..m | const std::vector<int>& param |
Cardinality | Declaration |
---|---|
0..1 | const std::string* param |
1..1 | const std::string& param |
0..* | const std::vector<std::string>& param |
1..* | const std::vector<std::string>& param |
n..m | const std::vector<std::string>& param |
Cardinality | Declaration |
---|---|
0..1 | const MyClass* param |
1..1 | const MyClass& param |
0..* | const std::vector<MyClass*>& param |
1..* | const std::vector<MyClass*>& param |
n..m | const std::vector<MyClass*>& param |
Cardinality | Declaration |
---|---|
0..1 | MyDatatype* param |
1..1 | MyDatatype param |
0..* | const std::vector<MyDatatype>& param |
1..* | const std::vector<MyDatatype>& param |
n..m | const std::vector<MyDatatype>& param |
Cardinality | Declaration |
---|---|
0..1 | const MyDatatype* param |
1..1 | const MyDatatype& param |
0..* | const std::vector<MyDatatype*>& param |
1..* | const std::vector<MyDatatype*>& param |
n..m | const std::vector<MyDatatype*>& param |
Note 1: Where the table states integer, this can be any basic type (integer, char, float, boolean).
Note 2: An element typed undefined is generated as void*.
The following liste shows the generated declaration for a parameter named “param”. Please note that no const values are used due to the “InOut” mode.
Cardinality | Declaration |
---|---|
0..1 | int* param |
1..1 | int& param |
0..* | std::vector<int>& param |
1..* | std::vector<int>& param |
n..m | std::vector<int>& param |
Cardinality | Declaration |
---|---|
0..1 | std::string* param |
1..1 | std::string& param |
0..* | std::vector<std::string>& param |
1..* | std::vector<std::string>& param |
n..m | std::vector<std::string>& param |
Cardinality | Declaration |
---|---|
0..1 | MyClass* param |
1..1 | MyClass& param |
0..* | std::vector<MyClass*>& param |
1..* | std::vector<MyClass*>& param |
n..m | std::vector<MyClass*>& param |
Cardinality | Declaration |
---|---|
0..1 | MyDatatype* param |
1..1 | MyDatatype& param |
0..* | std::vector<MyDatatype>& param |
1..* | std::vector<MyDatatype>& param |
n..m | std::vector<MyDatatype>& param |
Cardinality | Declaration |
---|---|
0..1 | MyDatatype* param |
1..1 | MyDatatype& param |
0..* | std::vector<MyDatatype*>& param |
1..* | std::vector<MyDatatype*>& param |
n..m | std::vector<MyDatatype*>& param |
Note 1: Where the table states integer, this can be any basic type (integer, char, float, boolean).
Note 2: An element typed undefined is generated as void*.
Automatic guessing on return parameters happens when C++ Designer is generating the code for a return parameter for which the automatic generation flag is set.
The following table shows the generated declaration for a return parameter.
Cardinality | Declaration |
---|---|
0..1 | int* |
1..1 | int |
0..* | int* |
1..* | int* |
n..m | int* |
Cardinality | Declaration |
---|---|
0..1 | std::string* |
1..1 | std::string& |
0..* | std::string* |
1..* | std::string* |
n..m | std::string* |
Cardinality | Declaration |
---|---|
0..1 | MyClass* |
1..1 | MyClass& |
0..* | MyClass* |
1..* | MyClass* |
n..m | MyClass* |
Cardinality | Declaration |
---|---|
0..1 | MyDatatype* |
1..1 | MyDatatype |
0..* | MyDatatype* |
1..* | MyDatatype* |
n..m | MyDatatype* |
Cardinality | Declaration |
---|---|
0..1 | MyDatatype* |
1..1 | MyDatatype& |
0..* | MyDatatype* |
1..* | MyDatatype* |
n..m | MyDatatype* |
Note 1: Where the table states integer, this can be any basic type (integer, char, float, boolean).
Note 2: Return parameters never return a collection, but simply a pointer to the first element of an array. This is because no assumptions can be made by C++ Designer about the internal implementation of the method returning the parameter. In real life, a reference to the container will most often be returned.
Note 3: For returned values with a minimum cardinality of 0, a pointer is returned, which allows the expression of the absence of any useful value (in which case a null pointer can be returned).
With Modelio C++ Designer, you can conveniently browse doxygen documentation for particular model elements.
To generate documentation, simply select a doxygen target from the DeploymentData package, and launch the “Generate doxygen documentation” command.
Launching the “Generate doxygen documentation” command
Modelio C++ Designer lets you directly browse documentation using your default web browser for classes, interfaces and namespace producing packages. For attributes, operations, parameters and other structural features, Modelio C++ Designer presents the documentation of the owner class. Simply launch the "Visualize doxygen documentation" command.
The Modelio C++ Designer GUI can be used to conveniently enter and modify brief summary and detailed documentation notes directly from the “C++” property view and from the dedicated tab of the C++ edition dialog boxes.
To document a model element, simply select it and then enter the associated documentation text in the “Summary” and “Documentation” fields of the “C++” property view.
Modelio C++ Designer translates the contents of notes as follows:
Notes entered in the “Summary” field are translated into the “\brief” doxygen tag. Summary notes should contain only one paragraph.
Notes entered in the “Documentation” field are translated into ordinary doxygen comments. Description notes can contain any number of paragraphs and doxygen tags.
Documentation for UML operation parameters is automatically grouped with the operation doxygen comment.
The “Summary” and “Documentation” fields in the “C++” property view
The “C++” property view is very practical when entering single line notes, but less convenient for complex multi-line documentation notes.
For optimal comfort and convenience, you can enter multi-line documentation notes in the C++ edition dialog boxes.
The “Documentation” tab of a C++ edition dialog box
The “Documentation” tab is present in all C++ edition dialog boxes.
Documentation can also be entered without using the Modelio C++ Designer GUI. You can enter summary notes in “comment” notes and documentation notes in “Cxx.Doc.Doxygen” or “description” notes.
Modelio C++ Designer produces the following code for the “TaskWindow” class, where the summary and documentation notes are injected as a doxygen comment with automatically generated “brief” tag and our tags.
1//includes for used library types
2#include <cstringt.h>
3#include <afxwin.h>
4#include <afxtempl.h>
5#include <afxcoll.h>
6
7//automatic includes (friends, associated classes, etc)
8#include "MyPlanner/CWnd.h"
9#include "MyPlanner/ITaskView.h"
10#include "MyPlanner/Task.h"
11
12namespace MyPlanner
13{
14 /**
15 \brief
16 Task visualization window
17
18 The class is used to graphically represent Task data.
19 Namely, the class is used to render:
20 - task information
21 - task status
22 - subtasks
23 - resources
24 \todo
25 Implement message handlers
26 **/
27 class TaskWindow : public CWnd, public ITaskView
28 {
29 //...
30 private:
31 CString displayTitle;
32
33 public:
34 CDC dc;
35
36 //associations
37
38 public:
39 Task* task;
40 CMap<CString,CString&,CBrush,CBrush&> brushResource;
41
42 //operations
43
44 public:
45 TaskWindow();
46 TaskWindow(const TaskWindow& value);
47 TaskWindow& operator =(const TaskWindow& value);
48 ~TaskWindow();
49 void formatDisplayTitle(std::string& FormatStr);
50 CDC getDc();
51 afx_msg int OnCreate(CREATESTRUCT* lpCreateStruct);
52
53 //non-modeled members
54
55 protected:
56 DECLARE_MESSAGE_MAP()
57 };
58
59}
60
With Modelio C++ Designer, you can conveniently enter documentation for application models. Documentation is produced in doxygen HTML format, which is widely used in the C++ world. This format is convenient for browsing and can be easily converted to other formats, such as CHM.
Modelio C++ Designer manages the documentation management process as follows:
Documentation generation targets provide the options used to drive doxygen behaviour. Modelio C++ Designer translates these options into the doxygen command file, which is processed by the doxygen binary.
Note: If you are using Windows, the Windows version of doxygen must be used. Don’t use the version of doxygen provided by the Cygwin tool.
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.BasicType | Basic Type | Type | Indicates a basic type that will be converted to a specific type in the current type package. |
Name | Label | Behaviours |
---|---|---|
Cxx.CLI.AssociationEndProperty | CLI Property | Indicates that this element represents a CLI property. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.CLI.Abstract | Abstrac | N/A | Indicates that the property is abstract. |
Cxx.CLI.GetterVisibility | Getter visibility | Visibility kind | Specifies the visibility of the generated getter. |
Cxx.CLI.Override | Override | N/A | Indicates that this property overrides a virtual one. |
Cxx.CLI.Sealed | Sealed | N/A | Indicates that the property is sealed. |
Cxx.CLI.SetterVisibility | Setter visibility | Visibility kind | Specifies the visibility of the generated setter. |
Name | Label | Behaviours |
---|---|---|
Cxx.CLI.GetterCode | Getter code | Contains the code to insert in the generated getter. |
Cxx.CLI.SetterCode | Setter code | Contains the code to insert in the generated setter. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.BasicType | Basic Type | Type | Indicates a basic type that will be converted to a specific type in the current type package. |
Name | Label | Behaviours |
---|---|---|
Cxx.CLI.AttributeProperty | CLI Property | Indicates that this element represents a CLI property. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.CLI.Abstract | Abstract | N/A | Indicates that the property is abstract. |
Cxx.CLI.GetterVisibility | Getter visibility | Visibility kind | Specifies the visibility of the generated getter. |
Cxx.CLI.Override | Override | N/A | Indicates that this property overrides a virtual one. |
Cxx.CLI.Sealed | Sealed | N/A | Indicates that the property is sealed. |
Cxx.CLI.SetterVisibility | Setter visibility | Visibility kind | Specifies the visibility of the generated setter. |
Name | Label | Behaviours |
---|---|---|
Cxx.CLI.GetterCode | Getter code | Contains the code to insert in the generated getter. |
Cxx.CLI.SetterCode | Setter code | Contains the code to insert in the generated setter. |
Name | Label | Behaviour |
---|---|---|
Cxx.CLI.Attribute | CLI Attribute | Indicates that this class defines a CLI attribute. |
Cxx.CLI.Class | CLI Class | Indicates that this element is a CLI class. |
Cxx.CLI.DelegateContainer | CLI Delegate Container | Indicates that this element is a container for delegates. |
Cxx.CLI.Indexer | CLI Indexer | Indicates that this class is a CLI indexer. |
CxxClass | C++ Class | Indicates that this element is a C++ class. |
Name | Label | Parameters | Behaviour |
---|---|---|---|
Cxx.Class.Friend | Friend | Class name | Specifies the name of the friend class. |
Cxx.ClassTemplate.Instantiate | Instanciation parameters | Specifies class template instanciation parameters. | |
Cxx.DeclarationSpecifier | Declaration Specifier | Declaration Specifier | Declares a class import and export for Windows DLL.
Puts the parameter value between the class keyword and the name of the class. For example : the class MyFacade is tagged Cxx.DeclarationSpecifier("DLLExport"). The header will be generated with : class DLLExport MyFacade |
Cxx.Struct | Is a structure | N/A | Specifies that a class must be generated as a structure instead of a class. |
Cxx.Union | Is a union | Extends clause | Specifies that a class must be generated as a union instead of a class. |
Name | Label | Behaviour |
---|---|---|
Cxx.Class.CopyConstructor.Code | Copy constructor code | Contains the code to insert into the copy constructor. |
Cxx.Class.Destructor.Code | Destructor code | Contains the code to insert into the destructor. |
Name | Label | Parameters | Behaviour |
---|---|---|---|
Cxx.CLI.Visibility | Visibility | Visibility kind | Specifies the visibility of this class. |
Name | Label | Behaviour |
---|---|---|
Cxx.CLI.Attribute | Attributes | Contains all the attributes defined on that class. |
Name | Label | Parameters | Behaviour |
---|---|---|---|
Cxx.CLI.Attribute.AllowMultiple | Allow Multiple | N/A | Allows multiple C# attributes on a field. |
Cxx.CLI.Attribute.Inherited | Inherited | N/A | Adds the attribute inheritance. |
Cxx.CLI.Attribute.Targets | Targets | The targets of the attribute. | Adds an attribute target to the class. |
Name | Label | Behaviours |
---|---|---|
CxxDataType | C++ DataType | Indicates that this data type is generated as a typedef. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.DataType.Anonymous | Is anonymous | N/A | Indicates that the name of the DataType is never used, but rather its definition instead. |
Name | Label | Behaviours |
---|---|---|
Cxx.FileGroup | FileGroup | Indicates that several classes must be generated in the same file. |
Name | Label | Behaviours |
---|---|---|
Cxx.CLI.Enumeration | CLI Enumeration | Indicates this enumeration is a CLI enumeration. |
CxxEnumeration | C++ DataType | Indicates this enumeration is generated. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.CLI.EnumerationType | Enumeration Type | Type | Specifies the enumeration's type. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.Generalization.ParentVisibility | Parent visibility | A visibility | Specifies the visibility of the super class. Can be public, protected or private. |
Cxx.Generalization.Virtual | Is virtual | N/A | Specifies a generalization as being virtual. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.Class.Extends | Extends | Classes | Textually specifies the extends clause of a class, including visibility and optional virtual clause. |
Name | Label | Behaviours |
---|---|---|
Cxx.Class.Member.Private | Private members | Contains code to insert into the private declaration of the class. |
Cxx.Class.Member.Protected | Protected members | Contains code to insert into the protected declaration of the class. |
Cxx.Class.Member.Public | Public members | Contains code to insert into the public declaration of the class. |
Name | Label | Behaviours |
---|---|---|
Cxx.CLI.Interface | CLI Interface | Indicates that this element is a CLI interface. |
CxxInterface | C++ Interface | Indicates that this element is a C++ class containing only virtual functions. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.Name | C++ Name | string | Specifies a C++ name for the parameter. |
Cxx.Bind | Bind | Type | Specifies template instanciation parameters. |
Cxx.Container | Container | Container name | Specifies a container to use if the element has a cardinality of *. |
Cxx.Container.Pointer | Container pointer | & or * | Specifies the pointer modifier to use for the container declaration. Must be & or *. |
Cxx.Container.Specifier | Container specifiers | C++ specifier | Owns a specifier for the container of the element (for example, const). |
Cxx.GenFullName | Generate full name | N/A | Specifies that the element must be generated with a namespaced type. |
Cxx.Lib | Library | N/A | Specifies the name of the library to use to declare the element. |
Cxx.Lib.ImportTypes | Import type from library | Type identifier | Uses a specific type from a specific type library. |
Cxx.NoCode | Do not generate | N/A | Specifies that the element must not be generated by Modelio C++ Designer. |
Cxx.Pointer | Pointer | & or * | Specifies the pointer modifier to use. Must be & or *. |
Cxx.Specifier | Specifiers | C++ specifier | Owns a specifier for the element (for example, const). |
Cxx.TypeExpr | Manual declaration | declaration | Specifies the declaration to use for the element. You can use $name to automatically insert the name of the element into the declaration. |
Cxx.Use.Class | Use class | Classes | Adds an include towards a class in the body or the header. |
Cxx.Use.Package | Use package | Packages | Adds an include towards a package in the body or the header. |
Name | Label | Behaviours |
---|---|---|
Cxx.Body.Bottom | Body bottom | Contains code to insert at the bottom of the body file. |
Cxx.Body.Top | Body top | Contains code to insert at the top of the body file. |
Cxx.Code | C++ Code | Contains the operation's implementation code. |
Cxx.Doc.Doxygen | Doxygen documentation | Contains the text to insert in the doxygen comment. |
Cxx.Header.Bottom | Header bottom | Contains code to insert at the bottom of the header file. |
Cxx.Header.Top | Header top | Contains code to insert at the top of the header file. |
Cxx.Use.Body | Body uses | Uses to add in the body file. |
Cxx.Use.Header | Header uses | Uses to add in the header file. |
Cxx.Value | Default value | Specifies a default value. |
Name | Label | Behaviours |
---|---|---|
Cxx.External | External | Indicates this element represents a class that is not modeled, usually to include it from another class. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.GenerateHeaderFile | Generate header file | N/A | Specifies that a header file containing the given includes must be generated. |
Cxx.IncludePath | Include paths | File names | Indicates the includes to be used for this element. |
Name | Label | Parameters | Behaviours |
---|---|---|---|
Cxx.CLI.NewOperation | CLI New | N/A | Indicates that this function does not override a base class method. |
Cxx.Operation.Disposition | Is inline | inline | Specifies the disposition of an operation (inline or not). The parameter must include “inline”. |
Cxx.Operation.Explicit | Is explicit | N/A | Specifies that the constructor must be generated as an explicit constructor. |
Cxx.Operation.Throws | Throws | Exception name | Specifies a set of exceptions thrown by the operation. |
Cxx.TypeExpr.Body | Content declaration | Type | Manual declaration. |
Name | Label | Behaviours |
---|---|---|
Cxx.Operation.Constructor.Base | Constructor Base | Contains code to insert at constructor transmission. |
Cxx.Operation.Postcondition | Postcondition | Specifies a postcondition of the operation. |
Cxx.Operation.Precondition | Precondition | Specifies a precondition of the operation. |
Cxx.Operation.Returned | Returned | Contains the operation's return statement. |
Name | Label | Behaviours |
---|---|---|
Cxx.Accessor | Accessor | Indicates this operation is an automatically managed accessor. |
Cxx.CastOperator | Cast Operator | Specifies the operation as a cast operator redefinition. |
Cxx.CLI.Delegate | CLI Delegate | Indicates this operation is a CLI delegate. |
Cxx.CLI.StaticConstructor | CLI Static Constructor | Indicates this operation is a static CLI constructor. |
Cxx.Operator | Operator | Specifies the operation as an operator redefinition. |
Name | Label | Behaviour |
---|---|---|
Cxx.CLI.Package | CLI Package | Indicates this element is a CLI package. All elements created in this package will be CLI elements by default rather than UML elements. |
CxxPackage | C++ Package | Indicates this element is a C++ package. All elements created in this package will be C++ elements by default rather than UML elements. |
Name | Label | Parameters | Behaviour |
---|---|---|---|
Cxx.Package.DirectoryName | Directory name | Include file | Specifies a name for the directory corresponding to this package. |
Cxx.Package.GenInterface | Generate interface | N/A | Automatically generates forward declaration of the public element of the package in the package header file. |
Cxx.Package.NoDirectory | No directory | N/A | Specifies that the package must not be generated as a directory. |
Cxx.Package.NoNamespace | No namespace | N/A | Specifies that the package must not be generated as a C++ namespace. |
Name | Label | Behaviour |
---|---|---|
Cxx.Body.NamespaceMember | Header namespace member | Contains code to insert into the namespace implementation in the package body file. |
Cxx.Header.NamespaceMember | Body namespace member | Contains code to insert into the namespace declaration in the package header file. |
Name | Label | Behaviour |
---|---|---|
Cxx.CLI.Event | CLI Event | Specifies this signal represents a CLI event. |
Name | Label | Parameters | Behaviour |
---|---|---|---|
Cxx.CLI.StaticEvent | Static event | N/A | Indicates this event is static. |
Name | Label | Behaviour |
---|---|---|
Cxx.CLI.Event.EventBlock | Event block | Contains the content of the event block. |
Name | Label | Behaviour |
---|---|---|
Cxx.CLI.TemplateParameter | CLI Template Parameter | Adds a constraint clause to the template. |
Name | Label | Parameters | Behaviour |
---|---|---|---|
Cxx.CLI.ConstraintClause | Constraint Clause | Clause | Adds a constraint clause to the template. |
The Introduction topic of the Modelio C++ Designer user guide is where you’ll find everything you need to know about the main features of this module.
The Introduction topic contains the following sections:
The Modelio C++ Designer Tour topic of the Modelio C++ Designer takes you on a detailed tour of all the features of this module.
Starting out by describing how you can prototype an application, this chapter goes on to describe in detail project and target management, application implementation and documentation.
The Modelio C++ Designer Tour topic contains the following chapters and sections:
The Prototyping An Application topic of the Modelio C++ Designer user guide describes how to get started with the C++ Designer module, and details the main concepts it implements.
The Prototyping An Application topic contains the following sections:
The Managing Projects And Targets topic of the Modelio C++ Designer user guide is where you’ll find everything you need to know about build project and target management, including details on the different options you can use.
The Managing Projects And Targets topic contains the following sections:
The Implementing An Application topic of the Modelio C++ Designer user guide brings you everything you need to know about how to implement your applications using the C++ Designer module.
The Implementing An Application topic contains the following sections:
The Documenting An Application topic of the Modelio C++ Designer describes how to quickly and efficiently document your C++ application models with the C++ Designer module, before generating documentation in doxygen format.
The Documenting An Application topic contains the following sections:
The Advanced Modelio C++ Designer Features topic of the Modelio C++ Designer user guide describes in detail the advanced features provided by this module.
From automatic guessing, to accessor generation, this chapter is where you’ll find everything you need to know to use advanced C++ Designer features. Variants are described in detail, as are UML extensions.
The Advanced Modelio C++ Designer Features topic contains the following chapters and sections:
The Automatic Guessing topic of the Modelio C++ Designer user guide describes exactly what happens to different elements during code generation when the automatic generation flag is set.
The Automatic Guessing topic contains the following sections:
The Generating Accessors topic of the Modelio C++ describes the accessors generated by C++ Designer when the “Create accessors” command is activated. Accessors are created in the model, meaning they are created in the form of modeled methods, and they are automatically decorated for subsequent C++ generation.
The Generating Accessors topic contains the following sections:
The Variants topic of the Modelio C++ Designer user guide presents in detail variants, which are used to implement and/or customize advanced code generation from a UML model. Among the concepts described are product file and template syntax, and XML schemas for type libraries and type guessing.
The Variants topic contains the following sections:
The UML Extensions topic of the Modelio C++ Designer user guide lists the various UML extensions (tagged values, notes and stereotypes) which can be used to annotate different elements.
The UML Extensions topic contains the following sections:
The Modelio C++ Reverser topic of the Modelio C++ Designer user guide describes in detail the features provided by the Modelio C++ Reverser module.
From information on reverse modes and how to launch reverse operations to details on UML equivalence and restrictions, this chapter is where you’ll find everything you need to know to use C++ Reverser.
The Modelio C++ Reverser topic contains the following sections:
A variant is a customization unit for the C++ module that must be declared in a C++ project before being used.
Variants can be divided into the following categories:
You can create your own type variants by writing custom type definitions and ACTs, or use the standard one, already included in C++ Designer.
The core of Modelio C++ Designer is an Active Code Template (ACT) processor. An ACT is a python or java file to be used at generation, which groups together:
ACTs are linked to model elements and output files by generation products. A generation product is a python script which links a UML metaclass, a condition defining when elements of this metaclass are translated, an ACT defining how these elements are translated, and the name of the output files containing the generated code.
You can define your own generation product by associating:
This means that you can define custom output, in addition to generated C++ code (IDL definitions or Java Native Interface wrappers, documentation in custom format, and so on).
Modelio C++ Designer can simultaneously support a number of generation product definitions.
A type library groups together definitions of types. A type definition associates a UML type with a C++ declaration, where the name of a UML structural feature or operation parameter will be substituted, and a list of include directives required to use the C++ type.
A UML type can be a primitive type, a data type or a collection type. Collection types are used to represent attributes and association ends with multiple cardinalities. The definition of a collection type also provides a high-level description of collection semantics – such as “OrderedCollection” – which is mapped to decorations automatically deduced from high-level UML model properties. This allows Modelio C++ Designer to automatically express high- level UML model semantics through particular C++ collection types.
When Modelio C++ Designer translates a structural feature or an operation parameter, it constructs the C++ declaration in accordance with its type and the current type library, and executes ACTs defined in the library, if they override the core ACTs defined for the element.
Code generation consists of producing C++ source files from UML model packages and classes. The actual rules used to map packages and classes to directories and files are presented later in this user guide.
UML model elements themselves are not enough to adequately describe C++ applications, for example, to describe the container (collection) types required to store multiple attributes or to describe pointer semantics. This means that before proceeding with C++ generation, the UML model must be decorated using additional model elements, tagged values, stereotypes and notes, which describe its C++-specific semantics and behavior.
Modelio C++ Designer supports two usage modes:
In conception mode, you focus on high-level model properties: application entities (modeled by UML classes), their relations (modeled by associations and links) and attributes, and their contracts (modeled by UML operations). When conceiving a model, you do not want to be distracted by low-level C++ details, such as the representation of a given multiple association by a vector or hash map, or the passing of a given operation parameter by a pointer or by a value.
To help you get quality C++ code directly from high-level models, Modelio C++ Designer automatically deduces C++-specific model decorations (C++ properties) from high-level UML model properties. Modelio C++ Designer then analyzes UML attributes, such as ordering and uniqueness, and applies C++ expert logics and best practices to derive reasonable decorations and subsequently C++ code, which adequately expresses model semantics.
With Modelio C++ Designer, you can immediately check your application models, and supports advanced modeling concepts, such as agile modeling. It tightens the link between application model and code, and extends the limits of UML model usage in the development process, thereby making UML models a practical development tool.
In implementation mode, you manage C++-specific application properties yourself, and specify C++ properties by manually decorating the model. The C++-oriented GUI hides the underlying UML extensions, making it easy to work in C++ terms and to immediately observe the effects of decorations on the generated C++ code.
Modelio C++ Designer supports transparent switching between the conception and implementation modes. You can reinforce the results of automatic deduction by adding your own decorations. For instance, once a model is automatically decorated and prototype C++ code has been generated, you can create C++ code notes, specifying detailed application behavior. You can indicate that a model element is to be ignored by automatic deduction and manually decorate it instead. This can be very practical when the results of automatic deduction do not correspond to your needs, for example, when you need to override a function defined in an external library, such as a message handler. Modelio C++ Designer transparently deduces decorations for high-level model parts and retrieves decorations specified by you.
The methods used to access the attributes of application classes provide an important part of the model semantics. However, these accessors are traditionally produced only at code level. Modelio C++ Designer is the first solution to support model-level accessors.
When you specify an “open” access mode (“read”, “write” or both) for an attribute or association end, Modelio C++ Designer automatically creates a UML operation in the respective class, which implements the respective access method, and then automatically deduces the C++ decorations of its return types and parameters, before automatically generating its [prototype] code.
The generation of accessors directly at UML model level lets you explicitly express access semantics at a high level, according to the access patterns you have chosen, thereby constituting a solid bridge between the application model and the code.
C++ requires that the creation and destruction behavior of each class object be explicitly specified. Modelio C++ Designer is the first solution to maintain these semantics at model level.
When you create a class, Modelio C++ Designer automatically creates its default constructor, destructor, and assignment operator, in accordance with C++ best practices. It automatically deduces the C++ decorations of their parameters, and generates some of their code and “to do” notes, reminding you to implement the creation and destruction of the class objects.
Constructors are implemented by design patterns. A creation pattern defines a set, particular signature and creation operation code, and possibly other model elements, such as class factories. These patterns are dynamic, meaning that when a class is updated (its name is changed), the related creation operations are automatically updated according to the pattern.
The automatic generation of creation operations at model level simplifies the essential task of implementing creation behavior, and constitutes a solid bridge between the application code and the model.
Through the transparent support of the conception and implementation modes and the support of dynamic patterns for model-level accessor and creation operations, you can benefit from a high level of automation, simplicity and flexibility, as well as full control over C++ code generation from the early development process steps onwards.
In addition to the conception and implementation modes and model-level dynamic patterns, the consistency of the UML model and the generated code is maintained through the model-driven generation mode.
This model-driven generation mode allows the generation of the entire C++ application from the model, as well as the retrieval of the code, inserted or edited externally using dedicated markers. This means that full model-code consistency is permanently ensured.
Compilation consists of producing binary output from generated C++ files. With Modelio C++ Designer, you can run compilation in one click from the property view.
This operation consists of producing documentation from generated C++ files. Documentation is produced in doxygen HMTL format, which is widely used in the C++ world. The doxygen format is convenient for browsing and can be easily converted to other formats, such as CHM.
UML model elements are documented by entering specific notes, which can contain doxygen tags. The Modelio C++ Designer GUI lets you conveniently enter and modify brief summary and detailed description notes directly from the C++ property view and from the dedicated tab in the C++ edition dialog boxes.
Modelio C++ Designer natively supports the management of build projects and targets.
A build target defines a build rule – code generation, documentation generation or compilation – for a UML model part. A target stores build options, such as the output path or default type library for C++ code generation.
A build project defines a set of build rules expressed by generation, documentation and compilation targets for the whole UML model or the model part, which is referred to as being manifested by the project.
A project can be associated with several build targets of the same type. You can easily run code generation and compilation for several target compilers, or generate debug and release code in different directories.
The UML model can be associated with several build projects, which can manifest intersecting model parts. This means that in one click you can generate code, produce documentation and compile only for necessary model parts, for instance to generate only library code or library tests.
Modelio C++ Designer provides a convenient project & target management GUI, which is used to create or modify projects, to associate model parts with projects, to create build targets and to specify their options.
Build projects and targets are represented at UML model level, meaning that they are handled directly as standard UML model elements.
The possibility of managing build projects and targets and expressing them in the UML model provides you with a simpler control application development process directly at UML level, thereby making the UML modeling environment ideal for C++ application development.
Welcome to the Modelio C++ Designer user guide!
Modelio C++ Designer is a solution for developing applications in UML 2 and C++ and for managing the development process. This user guide explains how to use the powerful application and platform development functions provided by Modelio C++ Designer.
Modelio C++ Designer provides the following functionalities:
The Cygwin tool must be installed on your workstation, in order for you to be able to use the following tools it provides:
Modelio C++ Designer works in a teamwork environment, with only simple restrictions to locked model elements.
In a common model, Modelio C++ Designer behaves as follows:
Consequently, the following two important rules must be respected:
If these two rules are not respected, Modelio C++ Designer will display an error message.
Modelio C++ Designer provides a convenient project management GUI, which is used to create or modify projects, associate model parts with projects, create build targets and to specify their options.
To create a project, select the root package and run the "Create C++ Designer element/Project" command from the context menu.
Creating a new project
All projects and targets are stored in the “DeploymentData” package. When a new project is created, Modelio C++ Designer automatically creates and associates three targets with the project:
Note: Default C++ code and doxygen documentation generation options and compilation options must be specified by Modelio C++ Designer parameters in order to properly initialize automatically created targets.
The project management dialog can also be accessed by selecting a target, and
running the "Edit project" command in
the C++ Designer property view.
Accessing the project edition window through the property view
With Modelio C++ Designer, you can create several projects for the model and several targets for a project. However, only one project can be active at any given time.
In the UML model navigator, the active project is identified with a specific icon . To change the active project, select another one and check the “Is active” tickbox in the C++ Designer property view.
The figure below shows the “Generation” tab of the project edition box, in which you can specify code generation options for the selected project.
The “Generation” tab of the project edition box
You can specify the following options:
Variant: The current variant utilized by the module for this project.
The default choice is the standard behaviour from C++ Designer’s resources path.
Type Library: The type library selected for the model elements manifested by the project.
Generated body files path: The root output directory for the body files produced by the target. A C++ code generation target produces body files for UML packages, classes and interfaces manifested by the project.
Generated header files path:The root output directory for the header files produced by the target. A C++ code generation target produces header files for UML packages, classes and interfaces manifested by the project.
Header files extension: The extension of the header files produced by the target.
Body files extension: The extension of the body files produced by the target.
Description: A description for the generation target. This will be visible in the model as a “description” note on the artifact.
Variants are used for advanced customization of the C++ Designer module.
The "Manage Variant" command opens the variant manager, used to install
new variants for the module, or uninstall existing variants.
The variant management box
1.: Opens a file chooser to install a new variant. You must select the variant directory itself, not the “act” or “product” directory.
2.: Removes the selected variant from the project. Obviously, it is impossible to delete the standard behaviour.
3.: Displays all variants available in this project.
4.: The directory corresponding to the selected variant. You should only use variants coming from the current project space, to avoid problems when exchanging your projects.
5.: The name of the selected variant, coming from the directory name.
6.: The extension of the body files produced by the target.
7.: The description of the selected variant.
Note 1: See the “Active Code Template” section for more info about creating new variants.
In the project edition box, the “Documentation” tab enables you to specify options for the documentation targets. To generate documentation, doxygen is used, and so these options are mapped to doxygen options.
The “Documentation” tab in the project edition window
In the left-hand part of the box, all documentation targets associated with
the current project are shown. You can create a new target using the
“Create documentation target” command, and delete a target using the
“Delete target” command. The tickbox in front of each target allows the
deactivation of this specific target. It will then be ignored when generating
documentation from the project.
When a target is selected, the right part becomes active with its data, and you can specify the following options:
Target Name: The target name. Modelio C++ Designer does not allow the creation of several targets with the same name.
Description: A description for the documentation target. This will be visible in the model as a “description” note on the artifact.
External Sources: The path to sources additionally processed by doxygen.
Production Path: The root output directory for the doxygen html documentation files produced by the target. Doxygen documentation files are produced for UML packages representing namespaces, for UML classes and interfaces manifested by the project.
Custom Doxygen Options: A set of custom doxygen options, which will be used to generate documentation. Modelio C++ Designer lets you specify default options using Modelio C++ Designer parameters, as well as adding your own options. Modelio C++ Designer provides a default set of doxygen options, which produce logical output.
In the project edition box, the “Build” tab enables you to specify options for the build targets. The build process uses makefiles, and so most of the options concern them.
The “Build” tab in the project edition window
In the left-hand part of the box, all build targets associated with the
current project are shown. You can create a new target using the
“Create build target” command, and delete a target using the
"Delete
target" command. The tickbox in front of each target allows you to deactivate
this specific target. It will then be ignored when building a project.
When a target is selected, the right part becomes active with its data, and you can specify the following options:
Target Name: The target name. Modelio C++ Designer does not allow the creation of several targets with the same name.
Description: Enter a description for the build target. This will be visible in the model as a “description” note on the artifact.
Filename: The file to build. If no errors are found during the compilation process, this file will be created at the end of the build process.
Type: The type of the binary file to build. Choose between “Executable” and “Library”.
Platform: The target platform for the file to build. Choose between “Windows”, “Linux” and “Unix”.
Path: Where the makefile will be generated.
Compiler options: The options to give to the compiler when building the application.
Linker options: The options to give to the linker when building the application.
Modelio C++ Designer solution supports the management of build projects and targets.
A target defines a build rule (code generation, documentation generation or compilation) for part of a UML model. A target stores build options, such as the output path or default type library for C++ code generation.
A build project defines a set of build rules, which are expressed by generation, documentation, and compilation targets, for the whole UML model or a part of the model, which we say is manifested by the project. The key principle is that project targets process only the manifested part of the model.
A project can be associated with several targets of the same type, meaning that you can easily run code generation and compilation of the same model part for several target compilers, or generate debug and release code in different directories.
The UML model can be associated with several build projects, which can manifest intersecting model parts. This lets you generate code, produce documentation and compile only those parts of the model which are necessary.
Build projects and targets are represented at UML model level through UML artefacts, allowing them to be handled directly in the same way as standard UML model elements.
Through the build project and target management feature, the application development process can be controlled directly from UML level, making the UML modeling environment a more solid basis for C++ application development.
To manifest the UML model elements to be processed by the project targets, simply edit a project and drag and drop the desired model elements from the Modelio model explorer into the “Project Contents” tab.
Only UML packages and classes can be manifested. Modelio C++ Designer creates manifestation links from the project artifacts to the dropped elements. As a result, the dropped elements will be processed by the C++ code generation, documentation and compilation targets associated with the project.
For example, we want to work separately with the Windows GUI part of our task management application. We want its code and documentation to be generated in a separate directory hierarchy, and we want to obtain code for it without generating the rest of the model. For this, we create the “Windows GUI” project, edit it, and drag and drop the “Windows” package into the “Project Contents” tab.
Manifesting the “Windows” package
You can un-manifest a model element from the project by selecting it in the
“Project Contents” tree and clicking on the "Remove from project" icon.
The “Project” tab of the project edition box is where you can enter the project name, workspace, subdirectory and description.
Creating a project
You can specify the following options:
Name: A name for your new project.
Modelio C++ Designer does not authorize the creation of several projects with the same name. If you enter a project name that already exists, Modelio C++ Designer displays an error message.
Workspace: The directory that will contain your project, along with any projects it uses and which have been provided by deployed model components.
We recommend that you keep an empty value; it will then use the value of “C++ Workspace” from the module settings.
Subdirectory: The project’s directory structure.
This directory structure contains an “SRC” directory containing your generated sources, a “bin” directory containing your compilation results, an “MkData” directory containing your generated makefiles and a “doc” directory containing project documentation.
Description: A description for the project you are creating. This will be visible in the model as a “description” note on the project artifact, allowing a quick overview of what each project is for.
By default, Modelio C++ Designer automatically deduces C++ decorations for parameters, attributes and association ends. However, in certain cases, deduced decorations may not correspond to what you actually intended. For example, you may want to override an externally defined virtual function, whose prototype does not correspond to that deduced by Modelio C++ Designer.
Modelio C++ Designer lets you mark the model elements to be avoided by the automatic deduction procedure. For this, the “C++” property view contains the “Automatic Decoration” tickbox.
The “Automatic Decoration” tickbox in the “C++” property view for a parameter
By default, the automatic decoration flag is always on.
By switching off the flag, you indicate that the selected element should not be taken into account in the automatic deduction process. In this case, Modelio C++ Designer only takes in account your decorations, and does not extrapolate new ones. To help you, deduced decorations are saved into the model, so that you can use them as a basis.
By switching on the flag, you indicate that the selected element is to be taken into account during the automatic deduction process. In this case, any decorations you have previously created are removed, and Modelio C++ Designer deduces new decorations at generation according to the rules defined.
Note: The automatic decoration strategy is also called automatic guessing.
See the corresponding section in the advanced part of this guide for more information.
Note 2: Editing C++ properties in the corresponding edition box sets the automatic decoration to off.
Structural features are attributes and associations between UML classes. Many existing C++ generators translate these as C pointers. However, C pointers are a poor choice to represent the semantics of relationships between model elements. Consequently, you have to implement this at code level and the model then diverges from code, or else you are distracted by low-level details, such as collection types for multiple attributes, storage by pointer or by value, and so on.
Modelio C++ Designer lets you focus on the high-level properties of application entity relationships and attributes. It automatically deduces C++ decorations, which specify collection and storage type, by analyzing the high- level properties of structural features (multiplicity, uniqueness, ordering, and qualifying attributes). These properties are often used to represent semantics on UML models.
We are now going to continue by creating a more detailed model of our
application prototype.
A more detailed model of our application prototype
The “Management” association links a “Person” to a “SimpleProject”. One manager can drive several projects, and there is only one manager for each project, so multiplicity is set to “1” and “*”. A manager can drive a project only once, and THE order of projects is not important, and we therefore specify uniqueness and no ordering for the “drivenProject” association end. For rapid access to a driven project, we qualify projects by project names, which are used as project keys.
By analyzing these properties, Modelio C++ Designer deduces that the “manager” association end is represented by simple pointer, and that the “drivenProject” association end is represented by “UnorderedMap”. Unordered maps are naturally represented by hash map collections, so Modelio C++ Designer automatically creates the respective declarations.
The model deduced by Modelio C++ Designer
In our example, an STL-type library is chosen by default. Consequently, Modelio C++ Designer produces the following definition of the “Person” class.
1 class Person
2 {
3 //...
4 public:
5 std::string FirstName;
6 std::string LastName;
7 std::string Email;
8 //associations
9
10 private:
11 std::hash_map<std::string,SimpleProject> drivenProject;
12
13 //operations
14
15 public:
16 Person();
17 Person(const Person& value);
18 Person& operator =(const Person& value);
19 ~Person();
20
21 //non-modeled members
22 };
Since C++ does not provide automatic memory management capabilities, you as a C++ user have to pay particular attention to the method you use to pass operation parameters, in order to avoid producing inefficient or incorrect code. For example, if a class parameter is passed by value, a copy constructor will be called, and the operation code will modify the copy, not the object itself, and so on.
Modelio C++ Designer lets you focus on high-level UML model properties without being distracted by low-level details, such as passing operation parameters by reference or by value or const qualifiers for “in” parameters.
Modelio C++ Designer analyzes a parameter’s type and sets the “by reference” passing modes for class or datatype parameters, as well as for parameters of non-primitive types defined in type libraries. In C++, this mode is implemented by adding a reference specifier to the parameter type. Modelio C++ Designer adds const specifiers to “in” parameters in order to explicitly express the fact that these parameters cannot be changed by the operation body, and const specifiers to “in” operations in order to explicitly express the fact that these operations do not change object state.
Consider our application prototype model. Continue by creating the “addSubTask” operation in the “Task” class operation, which takes a subtask to add and its number in the task work breakdown structure.
Before generation, Modelio C++ Designer automatically creates C++ decorations for its parameters. It adds a reference specifier to the type of the “SubTask” parameter, since this type is a class. It does not add a const specifier to this parameter, because it has “inout” semantics and can therefore be changed by the operation. It does not create a const specifier for the “wbsNum” parameter, because it is of primitive type. The integer type is translated to the C++ int type, which is passed by value, so there is no point in qualifying the “wbsNum” parameter as a const.
The “SubTask” parameter on the “addSubTask” operation
Modelio C++ Designer produces the following declaration of the “addSubTask” operation.
1 class Task
2 {
3 //...
4 public:
5 std::string name;
6 std::string wbsCode;
7
8 //associations
9
10 protected:
11 SimpleProject* project;
12 std::set<Task> subTask;
13 Task* owner;
14 std::list<HumanResource> resource;
15
16 //operations
17
18 public:
19 Task();
20 Task(const Task& value);
21 Task& operator =(const Task& value);
22 ~Task();
23 bool addSubTask(int wbsNum, Task& SubTask);
24
25 //non-modeled members
26
27 };
CLI attributes are defined through the C++ property view of a Class, by choosing the “CLI Attribute” value in the “Class kind” field.
CLI attribute property view
This view defines all options available on the CLI Attribute definition.
Allows multiple CLI attribute: Specifies whether or not the multiplicity of this CLI attribute is allowed.
Add attribute inheritance: Specifies whether or not the CLI attribute is inherited.
Attribute targets: Specifies the types of elements where the attribute is applicable.
Indexers are modeled through a UML class with two operations, “get” and “set”, to which the <<CLI Indexer>> stereotype is added.
A class stereotyped <<CLI Indexer>> will not be generated. It will simply be used to generate C# indexer get and set signatures and bodies.
To create an indexer, simply use the creation command from the contextual menu on a class, as shown below.
The “Create CLI Indexer” command
The following is a description of indexer accessors:
CLI events are modeled throught a UML signal to which the <<CLI Event>> stereotype is added. This signal must be a part of a class. Once a signal is stereotyped as an event, you can manage new values such as visibility (using the C++ property view) and delegate association (using Signal UML properties tab’s base field). An event must refer to a model’s delegate.
To create a CLI event, simply use the creation command from the contextual menu on a class, as shown below.
The “Create CLI Event” command
Delegates can be operations issued from delegate containers (classes stereotyped <<CLI Delegate Container>>) or delegates (operations stereotyped <<CLI Delegate>>). The related reference can be created through a drag and drop operation.
Delegate containers (classes stereotyped <<CLI Delegate Container>>) are used as containers of C# delegate types (operations stereotyped <<CLI Delegate>>). The generated delegates belong to the current parent element.
Making an operation <<CLI Delegate>> through the C++ property page will enable delegate signature and skip generation of all code notes attached to the operation.
CLI properties are modeled throught a UML attribute or association end, to which the <<CLI Property>> stereotype is added.
To obtain the property declaration with simplified syntax, you must not define any associated code notes (“CLI Getter Code”/“CLI Setter Code”), as if you do, a normal property will be generated.
The generator will automatically generate the get or set accessors according to which access mode has been selected.
Example of an integer property
The user can view C++ code generated for a selected model element by using the
"Edit header" and
"Edit body" buttons in the “C++” property view.
The “Edit header” and “Edit body” buttons in the “C++” property view
Let’s use these buttons to view the code generated for our “SimpleProject” class. We can see C++ constructors, operators and destructors generated from the model-level Coplien pattern, as well as “TODO” notes.
First of all, click on the "Edit header"
button to view the header file code generated for the “SimpleProject” class.
Code editor on the header file
Next, click on the "Edit body" button to
view the body file code generated for the “SimpleProject” class.
Code editor on the body file
The //begin of modifiable zone....
type of comment provides the markup
used to ensure model/code consistency. You can enter your code between
markups, and this code is then transferred back into the model to the
respective C++ code notes when the editor is saved. The code is read only
outside the marked zone, preventing unwanted modifications in the model.
Note: We recommend that the “Edit header” and “Edit body” buttons be used to enter implementation code, and that the code preview function be used to examine how UML model elements and their C++ properties (entered manually or deduced automatically) are translated to C++ code.
The first step in this tour is to create the model shown in figure below, by first creating the “My planner” package inside the root package, and then the “Task management” package inside the “My planner” package.
The model you are going to build
We are now going to continue by designating the main entities of our application (Project, Task, Human Resource and Person), and the relationships between these entities.
Let’s create a class to represent, for instance, projects. For this, create the “Project” class in the “Task Management” package.
Creating the “Project” class in the “Task management” package
Steps:
C++ does not provide automatic memory management and garbage collecting features, and its syntax does not permit the initialization of class members in declaration points. In most cases, you have to manually specify the creation and destruction semantics of your classes by writing constructors, destructors, and so on.
In order to avoid you forgetting creation operations for your objects, and to explicitly represent creation semantics at model level, Modelio C++ Designer automatically constructs UML creation operations when a class is created in the model. Modelio C++ Designer follows best C++ practices and uses a design pattern to create the so-called “Coplien form”:
Automatically constructed creation operations
For each operation, Modelio C++ Designer automatically creates C++ notes with “TODO” comments, suggesting that you provide their C++ implementation.
This model level pattern is dynamic, meaning that when you rename a UML class, its creation operations are renamed automatically.
The names of the creation operations are automatically modified when the class name is modified
To generate C++ code from the application model, select the element to
generate and click on the "Smart generate"
button in the C++ property view.
Launching C++ code generation using the button in the property view
If you have not created your own build project, Modelio C++ Designer automatically creates a default build project.
The default build project manifests the whole model, meaning that all model elements (except those marked by the “No Code” flag) are translated.
Its C++ code generation target uses the type library, compilation platform, output directories, header and body file extensions specified by Modelio C++ Designer parameters.
Its doxygen documentation target uses doxygen options and output directories specified by Modelio C++ Designer parameters.
Its compilation target uses makefile and compiler options specified by Modelio C++ Designer parameters.
Note: Default C++ code, documentation generation and compilation options must be defined using Modelio C++ Designer parameters, before using it for the first time.
The methods used to access the attributes of application classes constitute an important part of model semantics. However, these accessors are traditionally produced only at code level, with the result that the model differs from the code.
Modelio C++ Designer is the first solution to support the automatic creation of model-level accessors for structural features.
To create accessor operations for an attribute or association end, simply select the element in question and change its access mode from the UML property or the C++ Designer view.
Creating accessors from the C++ Designer view
Modelio C++ Designer implements accessors through design patterns. The current accessor pattern creates the “get” accessor operation if the feature has a “read” access flag (which corresponds to get access), and the “set” accessor operation if the feature has a “write” access flag (which corresponds to set access).
For a single cardinality primitive-type attribute, the get operation returns the attribute and the set operation sets its value. For an association end with single cardinality or a complex-type attribute, the get operation returns the reference to the target class and the set operation sets the feature value from a given const reference. For implemented collections with multiple cardinality features, get and set accessors are implemented by a single operation that returns the reference to the collection instance, which is a const if the “write” (set) access mode is not specified.
As an example, let’s run the “Create accessors” command on the “subtask” association end in the “Task” class, which has “*” multiplicity and read-only access semantics expressed by only the “read” (get) access mode. Modelio C++ Designer will then create the UML operation “getSubTask” in the “Task” class.
The result of running the “Create accessors” command on the “subtask”
association end in the “Task” class
Modelio C++ Designer copies the C++ decorations (that were automatically deduced for the “subtask” association end from its uniqueness and ordering properties) to the return type of the created accessor, additionally decorating it with the const specifier to represent read-only semantics. Modelio C++ Designer also creates the C++ code note, which implements default accessor implementation.
Modelio C++ Designer produces the following code for the “Task” class, which implements get access to the “subTask” member.
1 class Task
2 {
3 //...
4 public:
5 std::string name;
6 std::string wbsCode;
7
8 //associations
9
10 protected:
11 SimpleProject* project;
12 std::set<Task> subTask;
13 Task* owner;
14 std::list<HumanResource> resource;
15
16 //operations
17
18 public:
19 Task();
20 Task(const Task& value);
21 Task& operator =(const Task& value);
22 ~Task();
23 bool addSubTask(int wbsNum, Task& SubTask);
24 const std::set<Task>& getSubTask() const;
25
26 //non-modeled members
27 };
28
29 const std::set<Task>& Task::getSubTask() const
30 {
31 //modifiable zone @12953@30671900:493@T
32 return subTask;
33 //modifiable zone @12953@30671900:493@E
34 }
Model-level accessor management is dynamic, meaning that when an attribute is updated, its accessors are automatically updated too. If you rename an element, its accessors are automatically renamed. If you change an element’s access mode, the respective accessors are automatically created or deleted, and the const specifiers of collection accessors are automatically created or removed.
Generation of accessors directly at UML model level and their automatic synchronization with accessed elements is used to conveniently express access semantics at a high level, thereby creating a closer link between the application model and the code, starting from the prototyping stage.
Modelio C++ Designer provides two different generation options:
Forced generation runs a complete generation of the selected element (a class, a package or an entire project). In this case, all elements are generated.
To force the generation of C++ code, simply select the element you want to
generate and then click on the button in
the “C++” property view. C++ code is then generated for the selected element
and its contents.
Smart generation accelerates the generation process. When the smart generation option is used, Modelio C++ Designer looks at last modification dates and only generates elements that have been modified since the last generation operation, avoiding useless generation and speeding up the generation process.
To smart-generate C++ code, simply select the element you want to generate and
then click on the button in the “C++”
property view. C++ code is then generated for any elements (the selected
element or any of its sub-elements) that have been modified since the last
generation operation.
C++ element | UML element | Tagged Values |
---|---|---|
Bool | boolean | |
Char | char | |
Int | int | |
Long | long | |
Signed | Int | |
Short | short | |
Unsigned | int | {unsigned} |
Unsigned short | int | {short} {unsigned} |
Unsigned char | byte | |
Float | float | |
Double | double | |
Void* | undefined | |
Qualifier volatile | {volatile} | |
Qualifier const | {const} | |
Qualifier mutable | {mutable} |
C++ element | UML element | Tagged Values |
---|---|---|
Class | Class | |
Enumeration | Enumerated type | |
Enumeration elements | Enumeration Literal | |
Structure | Primitive class | {struct} |
Union | Primitive class | {union} |
C++ element | UML element |
---|---|
Namespace definition | Package |
Namespace alias | Reference on a package |
Namespace use (using) | Use link on the class associated with the file |
Namespace element use | Textual note on the class associated with the file |
C++ element | UML element | Tagged Values |
---|---|---|
Inheritance | Generalization | {private} |
Public inheritance | Generalization | |
Protected inheritance | Generalization | {protected} |
Private inheritance | Generalization | {private} |
Virtual inheritance | Generalization | {virtual} |
C++ element | UML element | Tagged Values | Multiplicity |
---|---|---|---|
Simple type | Attribute | 1 | |
Primitive class | Attribute | 1 | |
Defined or enumerated type member | Attribute | 1 | |
Non primitive class | Association role Composition | 1 | |
Pointer on char | String type attribute | 1 | |
Pointer on simple type | Attribute | {*} | 0..1 |
Primitive class pointer | Attribute | {*} | 0..1 |
Defined or enumerated type pointer | Attribute | {*} | 0..1 |
Non primitive class pointer | Association role | * | |
Reference on simple type | Attribute | {&} | 1 |
Primitive class reference | Attribute | {&} | 1 |
Defined or enumerated type reference | Attribute | {&} | 1 |
Non primitive class reference | Association role | 1 | |
Function pointer | Attribute with a C++TypeExpr note | 1 | |
Member pointer | Attribute with a C++TypeExpr note | 1 | |
Attribute member of table [n] of simple type | Attribute | multiplicity n | |
Attribute member of table [n] of primitive class | Attribute | multiplicity n | |
Attribute member of table [n] of enumerated or defined type | Attribute | multiplicity n | |
Attribute member of table [n] of non primitive class | Association role | multiplicity n |
C++ element | UML element | Tagged Values |
---|---|---|
Member function | Operation | |
Virtual member function | Operation | {virtual} |
Pure virtual member function | Abstract operation | |
Inline member function | Operation | {inline} |
Constructor | <<create>> operation | |
Destructor | <<destroy>> operation | |
Static member function | Class operation | |
Const member function | Operation whose passing mode is in |
C++ element | UML element | Tagged Values |
---|---|---|
Parameter by value | In/out parameter | |
Parameter by reference | In/out parameter | {&} |
Const parameter by reference | In parameter | {&} |
Pointer parameter | In/out parameter | {*} |
Char* parameter | String type in/out parameter | |
Ellipsis | Not translated | |
Parameter default value | Default value |
C++ element | UML element | Tagged Values |
---|---|---|
Void | No return parameter | |
No return indicated | "int" type return parameter | |
Return by value | Corresponding type return parameter | |
Return by reference | Corresponding type return parameter | {&} |
Return by pointer | Corresponding type return parameter | {*} |
C++ element | UML element | Tagged Values |
---|---|---|
Declaration of global variable | Class attribute | |
Declaration of external variable | Class attribute {extern} | |
Declaration of global variable | Class attribute | |
Declaration of static variable | Private class attribute |
C++ element | UML element |
---|---|
Non static global function | Class operation |
C++ element | UML element |
---|---|
Friend function declaration | No translation |
Friend class declaration | No translation |
C++ elements | UML elements | Tagged Values |
---|---|---|
Template class | Class + template parameter | |
Template operation | Operation + template parameter | |
Template static instantiation | Typedef | |
Template dynamic instantiation | Type of an attribute or operation parameter |
C++ | UML | Defined on |
---|---|---|
#include | C++BodyHeader or C++InterfaceHeader note | Classes associated with the file |
#define | No translation | |
Compilation directives | No translation | |
Directory structuring | Package |
Reversing an application or a part of an application is the operation that consists of creating a project that represents the application.
However, the reverse operation is not defined or scarcely defined for certain code constructions, either because they are not directly in tune with the UML norm, because they are truly specific to C++ or simply because they are not recommended for an object-oriented approach. This introduces some restrictions in the reverse tool’s ability to retrieve some parts of the code in the project.
The Modelio C++ Reverser reverse function is used to build a UML model from C++ source code. This UML model creation based on C++ code is carried out over several stages. Each of these stages will be presented and explained in this chapter.
The C++ code is transformed into a UML model annotated by a set of notes and tagged values from C++ Designer. Modelio uses specific modeling techniques for this model annotation. For information on equivalents, see “Equivalence between C++ source code and the UML model”.
Modelio C++ Reverser is a specific module that depends on the C++ Designer module and that must be deployed before performing any reverse operation.
The C++ Reverse functions over two stages:
The first phase consists of analyzing the C++ source code which is to be reversed. Once this analysis has been successfully completed, the application is modeled in the form of a tree. At this stage, nothing is imported into Modelio.
During the second phase, the elements selected by you are reversed in Modelio. The selected elements are reversed in the form of a UML model in Modelio, and the UML elements obtained are annotated in accordance with the C++ development module present in Modelio.
Diagram of C++ Reverse functioning
A class reversed into a project receives an identifier just like an object created in the Modelio browser or graphic editor.
If two users reverse the same class into two different projecting projects, Modelio will consider them as two different objects.
To avoid losing the links towards the reversed classes during the import, it is necessary to run the reverse in a reference modeling project from which each user imports the used classes.
The Modelio C++ Reverser reverse tool provides three reverse modes, each of which corresponds to a specific result in the produced project.
The “Simple structure” reverse mode is used to reverse only the packages and classes of the application.
This level makes it possible to use the reversed classes so as to:
This reverse mode is the fastest.
The “Complete structure” reverse mode makes it possible to reverse not only the same elements as the “Simple structure” mode, but also all the attributes, operations and associations of the reversed classes. However, operation contents and initializations (and code) are not reversed.
Reversed classes provide the same possibilities as with the “Simple structure” mode, but in this mode, it is also possible to:
The “Complete” reverse mode is used to reverse the application completely, including operation source code and attribute initialization.
This reverse mode creates a project from an application developed outside Modelio C++ Reverser, and allows you to continue its development within Modelio UML Modeler, thereby taking advantage of Modelio C++ Reverser generation.
For all three reverse modes, if there exist comments describing the application’s elements, these are also reversed.
For example, a comment on a class will be reversed into the project from “Simple structure” level upwards, whereas operation comments will only be reversed when the “Complete” reverse mode is used.
The following table details the elements reversed into the project at each level.
Simple structure | Complete structure | Complete | |
---|---|---|---|
Packages | X | X | X |
Classes | X | X | X |
Comments | X | X | X |
Attributes | X | X | |
Attribute comments | X | X | |
Attribute initializations | X | ||
Associations | X | X | |
Association comments | X | X | |
Association initializations | X | ||
Operations | X | X | |
Operation comments | X | X | |
Operation code | X |
Note: “X” indicates that the element is retrieved during the reverse operation.
The first step in the C++ reverse procedure is to select the model root and run the actual reverse command from the context menu.
Launching the “Reverse C++ application” command
After running the “Reverse C++ application” command, the “C++ Reverse” window then appears. This window is used to:
The first window to appear when the “Reverse application” command is launched is used to select the files you wish to reverse.
This window contains three zones:
Selecting directories to be reversed
Steps:
The second “C++ Reverse” window, which appears after you click on “Next” in the first window, is used to select any files or directories inside which you want to search for included files. These files will not be reversed.
This window contains two zones:
Selecting external include files and directories
Steps:
The third and final “C++ Reverse” window, which appears after you click on “Next” in the second window, is used to define the various options used during reverse operations.
This window contains two zones:
Defining reverse options
Steps:
After the reverse operation has been completed, a window named “Report of the reverse” appears. This window provides you with information on the reverse operation, as well as indicating any errors which were encountered.
C++ is a rich and complex language. Although it has been standardized by the ISO, its syntax and grammar remain very flexible, thereby allowing several different styles and variants in the code. Of course, these characteristics make it more complex to write C++ parsers and C++ reverse, with perfect parser reliability remaining a virtually impossible-to-reach goal. Even well-known C++ compilers introduce specificities and limitations of their own.This is also true for the Modelio C++ Reverser reverse tool.
Furthermore, the problem of reversing code to produce an equivalent UML model can encounter an additional difficulty, since C++ supports certain constructions that cannot easily be mapped to UML syntax, due to a lack of direct equivalence in UML.
To make the most of the Modelio C++ Reverser reverse tool, it is worth following some best practice tips. The following ten tips should help you improve the quality of your reversed model. Experience has proved that by taking these small precautions, you can reap significant benefits in the reversed model.
Reverse operations can be very time-consuming. Due to system resources, it can be a good idea to divide application sources into separate subsystems, before reversing them one by one.
Between every reverse, save the project. This will force the use of the Modelio garbage collector, thus freeing up memory, which can improve reverse speed.
Last but not least, reversing smaller parts greatly facilitates the diagnosis of potential problems.
When reversing a large application subsystem by subsystem (as described in the previous paragraph), dependencies between the different subsystems must be respected. In the subsystem dependency graph, subsystems that depend on no other subsystems should be reversed first, followed by subsystems depending only on them and so on. This process minimizes the number of partially instantiated objects, and reduces the risk of post-reverse object re- identification.
In most applications, several parameters, mainly macro definitions and include paths, are passed to the compiler. These options have an influence on the code that is truly compiled.
This “on the fly code transformation” is carried out by the C preprocessor. The Modelio C++ Reverser reverse tool can manage these options in the same way as a preprocessor, which means that passing the right parameters is a key to success. A good and pragmatic approach is to examine the options used when the application is compiled, for example, by looking at the makefile (or equivalent build file).
Reversing a complete application and reversing a compiled library are two different things.
In the case of reversing an application, the most important files are usually implementation files (*.cpp) . All the necessary header files are included in the implementation files, and only the implementation files themselves have to be selected in the “C++ Reverse” window.
For the reverse of a tier library, only header files should be selected. This will provide a structural UML model without C++ implementation notes that should be sufficient. Furthermore, in the case of tier libraries, implementation files are most often unavailable.
Some compilers use specific keywords. For example, Microsoft Visual C++ defines additional keywords such as “cdecl” or “stdcall”. These additional keywords are not known by the Modelio C++ Reverser reverse tool parser, as it conforms to the ANSI norm and not to the Microsoft variant.
In this situation, the best thing to do is to ignore these useless keywords, by providing them as an empty macro using the configuration interface.
A complete model can be very large, but a complete model is not always needed! Using reverse options, the reverse can be limited to only the core of your application, without including, for example, technical libraries. This means you can concentrate only on the business part of the model. Identifying and defining the parts of the application that are not to be reversed (in the second window of the reverse wizard) greatly helps keep the model within manageable limits in terms of its size.
The C++ reverse tool provides a set of extensions for some XML file compilers. Where several applications use the same specific extensions, it is recommended that these extensions be added to the configuration file for re-use.
The best reverse is achieved from the most standard code. The Modelio C++ Reverser reverse tool has been carefully designed to make the best use of the STL library. The use of STL containers gives the reverse tool the ability to rebuild correct associations.Non-standard constructs, although accepted by your compiler, can fool the Modelio C++ Reverser reverse tool parser and lead to poor results.
Sometimes simple modifications in the application code can significantly improve the quality of the model. For example, replacing a typedef by its expansion in its uses, or rewriting the declaration of an association can lead to a considerable enhancement of the model.
The C++ language has state-full and irregular grammar. This means that a notion of state exists in every statement in a file. When the parser finds an error, it can be confused about the current state and report false errors. Thus, in the list of errors, always look at the first one and correct it, since subsequent errors can, in fact, be false negatives.
During code analysis by C++ Reverser, errors are detected. These errors cause the analysis to fail, which means that no structure is visible in the “Show hierarchy” dialog box. You must therefore always make sure that the C++ code to reverse is compilable.
Comments for classes are contained in notes called “summary” and comments for attributes and operations are contained in notes called “description”.
C++ element | UML element | Remarks |
---|---|---|
Namespace alias | Reference on a package | Not handled |
Namespace use (using) | Use link on the class associated with the file | Not handled |
Namespace element use | Textual note on the class associated with the file | Not handled |
C++ element | UML element | Remarks |
---|---|---|
Parameter default value | Default value | Not handled |
Source element | UML element | Defined on | Remarks |
---|---|---|---|
#include | Use link | Classes associated with the file | Translated by a C++BodyHeader or C++InterfaceHeader note |
Directory structuring | Package | Not handled |
To build your application using the Modelio build system, you need Cygwin to use Make and GCC in order to compute dependencies.
Modelio C++ Designer provides the propagation system, which can generate a makefile with elements that are used by the application. If the compilation artifact manifests a class or a set of classes, only elements directly used by these classes will be compiled. For example, if you manifest the main class, all necessary classes will be compiled by propagation. This system is useful when you are producing several executables from one set of source files, with each executable using a subset of these sources.
The property view when a Makefile is selected
To generate a makefile, select the makefile target in the “DeploymentData”
package, and launch the "Generate Makefile" command in the C++ Designer
property view.
To build the application, launch the "Build" command. This will build
the application using the generated makefile. Dependency files are created
first to minimize future buildings, making the compiler rebuild the minimum
set of cxx files.
Note: If you need to use a specific library, you need the corresponding model component. You can import the model component into your project and then create a usage link between your compilation artifact and the compilation artifact of the provided library.
In C++, you can redefine standard operators, and for this, Modelio C++ Designer lets you declare this construct. Coplien form defined on a newly created class contains such an example.
<<Cxx.Operator>> operator =(in value : OperatorRedefinition):OperationRedefinition
To redefine a standard equality operator, the following steps should be carried out:
C++ also authorizes the creation of cast operators, to allow implicit conversion between types.
To define a cast operator, carry out the following steps:
The generated code will be as follows:
operator bool();
The C++ properties of UML model elements can be edited through the “C++”
property view and through C++ edition dialog boxes. The C++ edition dialog box
for a selected model element can be accessed by pressing the
"Edit properties" button.
The “Edit properties” button in the “C++” property view
C++ edition dialog boxes vary according to the model element selected. The C++ edition dialog box for a given element presents its C++ properties, grouped into different categories:
The next figure shows the C++ edition dialog box for a package.
The C++ edition dialog box for a package
To implement an application, application behaviour has to be described and implemented by attaching C++ code notes to UML operations.
Modelio C++ Designer conveniently lets you input ?++ code for UML operations directly in the operation edition dialog box. You simply enter your C++ code in the “Note Content” field of the “Operation Code” tab of the operation edition box. Modelio C++ Designer injects code entered in this field into the operation definition and generates the markers required to retrieve the code back into the model, if it is modified in the external editor.
As an example, we want to implement the assignment operator for the “Task” class in our application. For this, we open the C++ edition dialog box for the respective UML operation and input the operator code in the “Code” field.
Inputting the operator code
The entered code is injected into the definition of the assignment operator in the body of the “Task” class.
The “Return expression” field is used to input a return expression, which is always generated for the function independently of the function code. In our example, the standard return expression for assignment operators is input here. This note is generated automatically by the model-level creation operation pattern.
The “Base Constructor Code” field is used to input base construction clauses. Modelio C++ Designer automatically activates this field if the operation is detected as being a constructor (as a result of its name matching the owner class name or of the attached <<create>> stereotype).
Here is the generated code for this class:
1//class header file
2#include "MyPlanner/TaskManagement/Task.h"
3
4namespace MyPlanner
5{
6 namespace TaskManagement
7 {
8 Task::Task()
9 {
10 //modifiable zone @12366@30671900:196@T
11
12 //modifiable zone @12366@30671900:196@E
13 }
14
15 Task::Task(const Task& value)
16 {
17 //modifiable zone @12378@30671900:208@T
18 //TODO: write copy constructor code
19
20 //modifiable zone @12378@30671900:208@E
21 }
22
23 Task& Task::operator =(const Task& value)
24 {
25 //modifiable zone @12396@30671900:226@T
26 std::copy(value.getSubTask().begin(), value.getSubTask().end(), std::inserter(this.subTask, this.subTask.begin()));
27 std::copy(value.getResource().begin(), value.getResource().end(), std::inserter(this.resource, this.resource.begin()));
28 this.owner = value.getOwner();
29 this.name = value.getName();
30 this.wbsCode = value.getWbsCode();
31 //modifiable zone @12396@30671900:226@E
32
33 //modifiable zone @12397@30671900:227@T
34 return *this;
35 //modifiable zone @12397@30671900:227@E
36 }
37 //...
38 }
39}
Modelio C++ Designer lets you input ?++ code to be injected into the class header and body files directly in the class C++ edition dialog box. This can be useful to initialize global variables and implement utility functions used by the class and class members, which are not relevant to the model but which need to be provided.
To enter class-level C++ code, you simply enter it in the “Code” tab of the class C++ edition dialog box.
The “Code” tab of the C++ edition dialog box for the “TaskWindows” class
The “Placement” hierarchy defines where the input code will be injected:
Therefore, to input C++ code in the intended position in the class header or body file, you simply select the respective node in the “Areas” hierarchy and then type the code in the “C++” field.
For example, we want to inject the standard MFC macro “DECLARE_MESSAGE_MAP” into the header of the “TaskWindow” class, because this class represents the specific window (and is therefore inherited from the “CWnd” class) and should intercept window messages. The macro should be injected as a protected “member” of the class.
We open the “Code” tab of the class C++ edition dialog box, select the “Header –> Class [Class Members] –> Protected” node of the “Placement” hierarchy and type the intended code.
Modelio C++ Designer produces the following code for the “TaskWindow” class. The code entered for the “Header –> Class [Class Members] –> Protected” node of the “Placement” tree is injected into the protected part of the class.
1//includes for used library types
2
3#include <cstringt.h>
4#include <afxwin.h>
5#include <afxtempl.h>
6#include <afxcoll.h>
7
8//automatic includes (friends, associated classes, etc)
9#include "MyPlanner/GUI/Windows/CWnd.h"
10#include "MyPlanner/GUI/ITaskView.h"
11#include "MyPlanner/TaskManagement/Task.h"
12
13namespace MyPlanner
14{
15 namespace GUI
16 {
17 namespace Windows
18 {
19 class TaskWindow : public CWnd, public GUI::ITaskView
20 {
21 //...
22 private:
23 CString displayTitle;
24
25 public:
26 CDC dc;
27
28 //associations
29
30 public:
31 TaskManagement::Task* task;
32 CMap<CString,CString&,CBrush,CBrush&> brushResource;
33
34 //operations
35
36 public:
37 TaskWindow();
38 TaskWindow(const TaskWindow& value);
39 TaskWindow& operator =(const TaskWindow& value);
40 ~TaskWindow();
41 void formatDisplayTitle(std::string& FormatStr);
42 CDC getDc();
43 afx_msg int OnCreate(CREATESTRUCT* lpCreateStruct);
44 const CArray<CDC,const CDC&>& getAttr1() const;
45
46 //non-modeled members
47
48 protected:
49
50 //modifiable zone @16224@30671900:2404@T
51 DECLARE_MESSAGE_MAP()
52 //modifiable zone @16224@30671900:2404@E
53 };
54 }
55 }
56}
The "Edit header" and
"Edit body" buttons are available for
packages, classes, interfaces and operations.
For operations, the “Edit header” button opens the header of the owner class, and the “Edit body” button opens the body file of the owner file.
To edit C++ code in an editor, you simply select a model element and click on
the "Edit header" or
"Edit body" buttons in the “C++” property
view.
The editor containing the header or the body file contents is then opened. You can write implementation code in the editor, between markers. Save the editor to transfer the input code back into the model.
Note: You may prefer to input implementation code in an external editor that you can specify via the “External editor” Modelio C++ Designer settings. The model is then updated when you close this editor.
You can modify the application code outside the Modelio environment, for example, by editing it in your favorite IDE. Modelio C++ Designer lets you update the code notes belonging to model elements from externally modified code.
To update the code notes attached to a model element, just click on the
"Update" button in the “C++” property view.
Modelio C++ Designer reads the file (where the element’s code “lives”) and transfers the code between markers back into the respective notes.
Note: Only the code entered between markers generated by Modelio C++ Designer can be transferred back into the model. Any code input outside these markers is ignored!
By default, Modelio C++ Designer translates each UML package into a C++ namespace and generates a directory such that the hierarchy of namespaces and directories corresponds to the package hierarchy. However, it may be unnecessary at generated code level to have the granularity introduced at model level. Modelio C++ Designer lets you avoid namespace and/or directory generation for arbitrary chosen packages.
To avoid namespace generation, simply select a UML package and switch off the “Is a namespace” flag in the “C++” property view.
The “Is a namespace” option in the “C++” property view
After the “Is a namespace” option has been switched off, Modelio C++ Designer puts all the classes, interfaces, datatypes, enumerations and sub-packages defined in the package into the namespace of the owner package. If the owner package does not produce a namespace either, these entities are generated in the owner of the owner package, and so on. The root package always corresponds to the global namespace.
For example, let’s imagine we want to put all the classes defined in our application model into the “MyPlanner” namespace, which corresponds to the “MyPlanner” package. For this, we switch off the “Is a namespace” flag for the “TaskManagement”, “GUI” and “Windows” packages.
As a result, Modelio C++ Designer produces the following code, where all the classes represented in the model are defined in the “MyPlanner” namespace, for example, the “TaskWindow” class.
1//includes for used library types
2#include <cstringt.h>
3#include <afxwin.h>
4#include <afxtempl.h>
5#include <afxcoll.h>
6
7//automatic includes (friends, associated classes, etc)
8#include "MyPlanner/GUI/Windows/CWnd.h"
9#include "MyPlanner/GUI/ITaskView.h"
10#include "MyPlanner/TaskManagement/Task.h"
11
12namespace MyPlanner
13{
14 class TaskWindow : public CWnd, public ITaskView
15 {
16 //...
17 private:
18 CString displayTitle;
19
20 public:
21 CDC dc;
22
23 //associations
24
25 public:
26 Task* task;
27 CMap<CString,CString&,CBrush,CBrush&> brushResource;
28
29 //operations
30
31 public:
32 TaskWindow();
33 TaskWindow(const TaskWindow& value);
34 TaskWindow& operator =(const TaskWindow& value);
35 ~TaskWindow();
36 void formatDisplayTitle(std::string& FormatStr);
37 CDC getDc();
38 afx_msg int OnCreate(CREATESTRUCT* lpCreateStruct);
39
40 //non-modeled members
41
42 protected:
43 //modifiable zone @16224@30671900:2404@T
44 DECLARE_MESSAGE_MAP()
45 //modifiable zone @16224@30671900:2404@E
46 };
47}
To avoid directory generation, simply select a UML package and switch on the “No directory flag” in the “C++” property view. This will result in Modelio C++ Designer putting all the files produced for the package (the header and body files of its classes, interfaces and sub-packages) into the directory of the owner package. If the owner package does not produce a directory, the files are created in the directory of the owner of the owner package, and so on. The root package corresponds to the directory specified in the active generation target output path.
For example, let’s imagine that we want to put all the files generated for our application model into the “MyPlanner” directory, which is produced for the “MyPlanner” package. To do this, we simply switch on the “No directory” flag for the “TaskManagement”, “GUI” and “Windows” packages.
As a result, Modelio C++ Designer puts all the produced files into the “MyPlanner” directory.
All the produced files have been put into the “MyPlanner” directory
Modelio C++ Designer provides a system used to generate C++ code for part and ports. Parts and ports are UML 2 features designed to represent limited connexions in component architecture, as well as their integration through the connection of predefined plugs.
Modeling parts and ports
Some classes can publish or require certain information, declared by an interface. In the example provided in the figure above, the “Camera” class publishes an “iVision” interface as a provided interface. Thus, the camera publishes all this information to this channel. The “Arm” class publishes an “iControl” interface to obtain the information necessary for its use by a normalized channel.
To create a generable port on a class, simply create a port, name it and then specify required or provided interfaces, represented by lollipops. A single port can simultaneously have provided and required interfaces, as in the “Brain” example shown below.
Note: Don’t forget to specify interfaces to type the plug by selecting the lollipop and clicking on the UML properties.
Internally, every port is a class, which is automatically generated and does not appear in the model. The name of this class is normalized through the concatenation of the class name, the port name and the “Port” keyword.
Class | Port | C++ name of the port |
---|---|---|
Brain | Cmd | BrainCmdPort |
Camera | Vis | CameraVisPort |
Arm | Ctrl | ArmCtrlPort |
Every operation on the connection point must therefore be carried out using this port. This generated class is the access point to interfaces.
The port class provides access to provided and required interfaces. To retrieve the interface, an accessor is automatically created from “get”, the “Required” or “Provided” keyword and the type of the interface. For example, the “CameraVisPort” class contains an accessor named “getProvidedIVision ()”. Using these accessors, the class does not know the linked element. This system provides important separation of issues.
For example, if the brain takes the information from the vision device through the “Command” port, and in order to call the “move” command via the same port, a method must contain the following code:
1Void Brain::move () {
2 // Retrieving the command port
3 BrainCmdPort *cmdPort = getCmdPort ();
4
5 // getting the required plug
6 IVision vision = cmdPort->getRequiredIVision ();
7 Data d = vision.getData ();
8
9 // getting the provided plug
10 ICommand command = cmdPort->getProvidedIControl ();
11 Command.move (d);
12}
In a part/port model, a class will instanciate all classes for component assembly within its internal structure, and you, the user, just have to connect them. However, these elements must be coherent with the static model. For this reason, Modelio C++ Designer contains a wizard to help you to create this model.
To create an internal structure model, create an integration class. In the internal structure, simply create parts and set their type to instanciated classes. Next, launch the “Complete internal structure model” command. This command will complete the model by creating attributes in the class to refer to instances, by adding ports and provided/required interfaces relative to the static model of these classes, and by keeping the model consistent.
Once all these elements are instanciated, you should simply connect the elements in order to initialize the parts. When the class containing an internal structure is generated, this will automatically lead to generation of the instanciation and connection code. This means that you can concentrate on business code instead of integration code, which is totally generated.
Running the “Complete internal structure model” command
Note: The “Complete internal structure model” command can also update the internal structure model. If you change the static model of a class containing ports, for example by adding a new lollipop, the launch of this command will update those internal structures which use it.
In C++, there are many way to specify the usage of a class. These can be separated into two parts:
When you explicitly link two UML elements, either by typing an attribute or a parameter, or by creating an association, a usage is generated in the source code. Depending of the kind of usage, a forward declaration or an inclusion is generated. Modelio C++ Designer will always prefer forward declaration, in order to limit the dependencies of the generated file.
When you need to explicitly create an include, element imports are used. Element imports are the UML link that provides an element with visibility of another importable element. In the C++ language, this notion is expressed through #include directives.
The visibility of the element import designates the file where the include is to be generated:
Modelio C++ Designer provides a convenient new feature used to preview the C++ code that will be generated for a selected model element.
The preview field renders the C++ code according to the element properties that are currently set in the dialog box in real time. This means that you can immediately observe the effect of your input on the generated code and consequently avoid errors during the early stages of C++ application development.
For example, let’s edit the properties of a package. We will first preview the code that will be generated for this element.
Previewing the code that will be generated for a package
Note: The code preview function is implemented by specific code preview templates. These preview templates are consistent with code generation templates, so preview is always consistent with the generated code, even if the generation templates are modified.
To open the window in which Modelio C++ Designer settings are defined, run the “Modules” command from the “Configuration” menu.
Modelio C++ Designer provides seven sets of parameters, used to define options in the following areas:
The “General” parameter set
The list of paramters:
Default behavior for file retrieving:
generator behavior in the case where the file as been modified outside Modelio:
Generation path:
the default path that will be used for generated files.
Reload ACT automatically:
whether or not Cxx Designer will dynamically reload its ACT files at each
generation. Unchecking this option may improve generation performances.
Variant:
the default variant to use in the C++ projects.
Default type library:
the default library to use.
Activate ACT engine log:
the verbosity of the ACT engine. This is useful for ACT developers.
Use “description” notes instead of “Doxygen”:
the use of “description” type notes instead of “Cxx.Doc.Doxygen” type notes.
The “General” parameter set
The list of parameters:
Automatically generate Coplien form:
whether or not coplien form generation should be run automatically.
Automatically generate accessors: whether or not accessors should be created automatically.
The “External edition” parameter set
The list of parameters:
Use external editor:
whether or not an external editor should be used instead of the internal Modelio editor.
External Editor Command Line:
the command you should type to launch the external editor used to edit the files generated by Modelio.
* The “Makefile generation” parameter set
The list of parameters:
Compiler under windows:
the compiler to be used in the Windows makefile.
Linker under windows:
the linker to be used in the Windows makefile.
Library manager under windows:
the library manager to be used in the Windows makefile.
Compiler under linux
the compiler to be used in the Linux makefile.
Linker under linux
the linker to be used in the Linux makefile.
Library manager under linux
the library manager to be used in the Linux makefile.
Configuration
the choice between debug options and release options.
Directory for cygwin
the directory where Cygwin tools are located (G++ and Gnu/make).
The “Projects default values” parameter set
The list of parameters:
Default target platform:
the target compiler to generate for.
Default generated header files extension:
the extension that will be used for generated header files.
Default generated body files extension:
the extension that will be used for generated body files.
The “Doxygen” parameter set
The list of parameters:
Doxygen executable:
the location of the Doxygen executable.
Documentation browser:
the HTML browser that will be used to browse generated documentation.
Default Doxygen parameters:
the default parameters that will be used for Doxygen generation.
Default Doxygen output path:
the path where Doxygen documentation will be generated.
Note: Use Doxygen for Windows, not Doxygen from Cygwin.
The “Model components” parameter set
The list of parameters:
Add body files in the model component: whether or not body files should be added to the model component that you are packaging.
Add header files in the model component: whether or not header files should be added to the model component that you are packaging.
When implementing applications, you often need to refer to external sources, such as large external libraries, without explicitly representing them at model level or by type libraries. For example, you may want to re-use external code or plug your applications to an external environment by implementing/overriding external interfaces.
Modelio C++ Designer provides features that let you conveniently use external sources in application UML models.
You can specify the external headers to use in a model class in the “Externals” tab of the C++ edition dialog box for a class.
Specifying external headers in the “Externals” tab of the C++ edition dialog box on a class
Modelio C++ Designer injects the text entered in the “Header includes and using namespaces” field into the header file of the selected class, right after automatically generated include directives. Similarly, the text entered in the “Body includes & using namespaces” field is injected into the body (C++ source) file of the selected class, also after automatically generated include directives. Arbitrary C++ constructions are possible in those fields.
For example, let’s imagine that we want to use our custom logging service in the “TaskWindow” class. We do not want to represent the “MyLogger” class, which implements the service, at model level, because it is not relevant to our application, so instead we write the code in the “Body includes & using namespaces” field of the “TaskWindow” class edition box. We want our code to be injected into the body file to isolate usage of the logging service from other application parts. For this, we are going to enter our code in the “Body includes and using namespaces” field.
Entering our code in the “Body includes and using namespaces” field
The code entered in this text field is injected into the body file of the “TaskWindow” class.
1#include "MyPlanner/GUI/Windows/TaskWindow.h"
2
3//modifiable zone @13927@30671900:980@T
4#include "MyLog/MyLogger.hpp"
5
6#ifdef DEBUG
7#define MY_LOGGING_LEVEL 9
8#endif
9
10using namespace MyLogger;
11//modifiable zone @13927@30671900:980@E
12
13namespace MyPlanner
14{
15 namespace GUI
16 {
17 namespace Windows
18 {
19 TaskWindow::TaskWindow()
20 {
21 //modifiable zone @13351@30671900:701@T
22
23 //modifiable zone @13351@30671900:701@E
24 }
25
26 TaskWindow::TaskWindow(const TaskWindow& value)
27 {
28 //modifiable zone @13363@30671900:713@T
29 //TODO: write copy constructor code
30
31
32 //modifiable zone @13363@30671900:713@E
33 }
34
35 TaskWindow& TaskWindow::operator =(const TaskWindow& value)
36 {
37 //modifiable zone @13381@30671900:731@T
38 //TODO: write assignment operator code
39
40 //modifiable zone @13381@30671900:731@E
41
42 //modifiable zone @13382@30671900:732@T
43 return *this;
44 //modifiable zone @13382@30671900:732@E
45 }
46
47 TaskWindow::~TaskWindow()
48 {
49 //modifiable zone @13388@30671900:738@T
50 //TODO: write destructor code
51
52 //modifiable zone @13388@30671900:738@E
53 }
54
55 void TaskWindow::formatDisplayTitle(std::string FormatStr)
56 {
57 //modifiable zone @13845@30671900:918@T
58
59 //modifiable zone @13845@30671900:918@E
60 }
61 }
62 }
63}
Note: The “Externals” tab page is available only in the class C++ edition dialog box. To use external sources required for model operations or structural features, these must be specified for the owner classes.
You can specify a selected class to be inherited from an external non-modeled class in the “Externals” tab of the class C++ edition dialog box.
Modelio C++ Designer injects the text entered in the “External inheritance” field after the colon token in the selected class definition, thus producing the inheritance declaration. To include headers with the definition(s) of the specified external class(es), the “Header includes & using namespaces” field can be used.
We want the “TaskWindow” class to be a child of the “CWnd” class from the MFC library, which represents a window and provides services for windows management. It is not wise to define this class in the “MFC” type library, because inheritance from a datatype (which would represent the “CWnd” class on the model) does not seem semantically correct.
Therefore, we enter public inheritance declaration and include the external headers with the “CWnd” definition in the respective fields of the “TaskWindow” class C++ edition dialog box.
Entering public inheritance declaration and including external headers
Modelio C++ Designer produces the following code for the “TaskWindow” class. External inheritance declaration and headers are injected. By clicking the “Preview” button in the class C++ edition dialog box, we can obtain this code immediately.
1//includes for used library types
2#include <string>
3#include <cstringt.h>
4#include <afxwin.h>
5#include <afxcoll.h>
6
7//automatic includes (friends, associated classes, etc)
8#include "MyPlanner/GUI/ITaskView.h"
9#include "MyPlanner/TaskManagement/Task.h"
10
11//modifiable zone @13936@30671900:989@T
12#include "afxwin.h"
13//modifiable zone @13936@30671900:989@E
14
15namespace MyPlanner
16{
17 namespace GUI
18 {
19 namespace Windows
20 {
21 class TaskWindow : public CWnd, public GUI::ITaskView
22 {
23 //...
24 private:
25 CString displayTitle;
26
27 public:
28 CDC dc;
29
30 //associations
31
32 public:
33 TaskManagement::Task* task;
34 CMap<CString,CString&,CBrush,CBrush&> brushResource;
35
36 //operations
37
38 public:
39 TaskWindow();
40 TaskWindow(const TaskWindow& value);
41 TaskWindow& operator =(const TaskWindow& value);
42 ~TaskWindow();
43 void formatDisplayTitle(std::string FormatStr);
44
45 //non-modeled members
46
47 };
48 }
49 }
50}
To explicitly represent relations with external classes, it can be useful to include them in the model. These classes may have quite a complex structure, which ?an make them difficult to reverse or irrelevant to the modeling context.
To represent an external class in the model without specifying its full structure, you can set the “External code” flag in the class C++ edition dialog box. You then specify the code required to use the external class (include directives, macro commands) in the “External headers for external code” field. Instead of translating the modeled class structure, Modelio C++ Designer simply injects the code specified in the field into the class header file.
As a result, you can specify only the relevant parts of external classes. These parts do not need to be specified to exactly correspond to the external class sources.
For example, we want to explicitly model the fact that the “TaskWindow” class is the child of the “CWnd” class from the MFC library. We create the “CWnd” class in the model and create UML operations to explicitly represent certain message handlers that we want to override. We do not define exact message handler signatures, but rather simply show their names in the model.
The newly created “CWnd” class with its operations
To specify the “CWnd” class as being external, we set the “External code” flag and provide the headers required to use the “CWnd” class from the MFC library in the class C++ edition dialog box.
Specifying that the “CWnd” class is external
Modelio C++ Designer produces only the following code for the “CWnd” class (despite the fact that the “OnCreate” and “OnPaint” UML operations are presented in the model).
1#ifndef __CWnd_15265_H_INCLUDED
2#define __CWnd_15265_H_INCLUDED
3/*
4 * File type: Class header
5 * Class: CWnd
6 */
7
8#include "stdafx.h"
9#include "afxwin.h"
10#endif __CWnd_15265_H_INCLUDED
This header is included when the “CWnd” class is used, so the “CWnd” class external definition (provided in the “afxwin.h” file) is available to all the class clients. For example, the “CWnd” class external definition is available in the code generated for the “TaskWindow” class.
1//includes for used library types
2#include <afxtempl.h>
3#include <cstringt.h>
4#include <afxwin.h>
5#include <afxcoll.h>
6
7//automatic includes (friends, associated classes, etc)
8#include "MyPlanner/GUI/Windows/CWnd.h"
9#include "MyPlanner/GUI/ITaskView.h"
10#include "MyPlanner/TaskManagement/Task.h"
11
12namespace MyPlanner
13{
14 namespace GUI
15 {
16 namespace Windows
17 {
18 class TaskWindow : public CWnd, public GUI::ITaskView
19 {
20 //...
21 private:
22 CString displayTitle;
23
24 public:
25 CDC dc;
26
27 //associations
28
29 public:
30 TaskManagement::Task* task;
31 CMap<CString,CString&,CBrush,CBrush&> brushResource;
32
33 //operations
34
35 public:
36 TaskWindow();
37 TaskWindow(const TaskWindow& value);
38 TaskWindow& operator =(const TaskWindow& value);
39 ~TaskWindow();
40 void formatDisplayTitle(std::string& FormatStr);
41 CDC getDc();
42
43 //non-modeled members
44 };
45 }
46 }
47}
To interface the application with an external environment, it can be necessary to override certain virtual functions defined in external sources. It is often necessary to override message handlers defined in the external GUI library, such as MFC.
To override an external virtual function, carry out the following steps:
To match the parameter type with an external one, you should simply switch off the automatic decoration flag in the C++ edition dialog box for the parameter, before specifying the intended type specifiers in the same dialog box.
Note: If you change the deduced values of the “Pointer type”, “Container type” and “Collection pointer type” properties in the parameter edition dialog boxes, the automatic decoration flag is switched off by Modelio C++ Designer.
For example, in the “TaskWindow” class we want to override the “OnCreate” message handler defined in the “CWnd” parent external class.
The “OnCreate” message handler has the following declaration:
1class CWnd : public CCmdTarget
2{
3//...
4public:
5 afx_msg int OnCreate(CREATESTRUCT* lpCreateStruct);
6//...
7};
We create the “OnCreate” operation in the “TaskWindow” class, and then set its “lpCreateStruct” parameter to the “CREATESTRUCT” dummy datatype (this structure is defined in the “afxwin.h” header, so it is available as a result of inheritance from the external “CWnd” class).
Then, we manually specify the C++ properties of the “lpCreateStruct” parameter, to set its type as the pointer, and remove the const specifier.
Manually specifying the C++ properties of the “lpCreateStruct” parameter
As a result, Modelio C++ Designer switches off the automatic decoration flag for the “lpCreateStruct” parameter and instructs that automatic deduction for this model element should be skipped and that only C++ decorations specified by us should be used.
The last remaining action is to define the “afx_msg” specifier for the “OnCreate” operation. This specifier is set in the operation C++ edition dialog box.
Defining the “afx_msg” specifier for the “OnCreate” operation
As a result, Modelio C++ Designer produces the correct code for the “TaskWindow” class, where the “OnCreate” function does override the “OnCreate” message handler defined in the external “CWnd” class.
1//includes for used library types
2#include <cstringt.h>
3#include <afxwin.h>
4#include <afxcoll.h>
5
6//automatic includes (friends, associated classes, etc)
7#include "MyPlanner/GUI/Windows/CWnd.h"
8#include "MyPlanner/GUI/ITaskView.h"
9#include "MyPlanner/TaskManagement/Task.h"
10
11namespace MyPlanner
12{
13 namespace GUI
14 {
15 namespace Windows
16 {
17 class TaskWindow : public CWnd, public GUI::ITaskView
18 {
19 //...
20 private:
21 CString displayTitle;
22
23 public:
24 CDC dc;
25
26 //associations
27
28 public:
29 TaskManagement::Task* task;
30 CMap<CString,CString&,CBrush,CBrush&> brushResource;
31
32 //operations
33
34 public:
35 TaskWindow();
36 TaskWindow(const TaskWindow& value);
37 TaskWindow& operator =(const TaskWindow& value);
38 ~TaskWindow();
39 void formatDisplayTitle(std::string& FormatStr);
40 CDC getDc();
41 afx_msg int OnCreate(CREATESTRUCT* lpCreateStruct);
42
43 //non-modeled members
44
45 };
46 }
47 }
48}
In C++, typedefs are intensively used to introduce compiler-controlled simple types used as short names for complex types. Modelio C++ Designer lets you easily create typedefs of arbitrary complex types.
To create a C++ typedef, you simply create a UML datatype with the intended typedef name in the model. To specify the represented C++ type, the datatype is decorated by the “Cxx.TypeExpr” tagged value, and the C++ type expression is provided in the in the first tagged value parameter.
Because C++ permits very complex recursive type definitions, you can put the “$name” ACT macro in the type expression to specify exactly where the typedef name should be injected. If the “$name” macro is not specified, it is automatically added at the end of the type expression.
Modelio C++ Designer translates the UML datatype into the C++ typedef declaration by adding the “typedef” keyword and substituting the “$name” macro by the typedef name. The datatypes defined in a class are generated in the class definition, while the datatypes defined in a package are generated in the package namespace in the package header file.
For example, we want to define the task handlers in the “Task” class. Task handlers are specific functions that run when certain conditions in the task are satisfied, for example, when the task is completed or delayed. A task handler is represented by the pointer to a function, which takes the “Task” instance as a parameter and returns a Boolean status.
To represent the task handlers, we introduce the “TaskHandler” typedef in the “Task” class, which expresses this pointer to a handler function. We then create the “TaskHandler” datatype in the “Task” class and decorate this datatype by the “Cxx.TypeExpr” tagged value with the following expression:
1bool (*$name)(Task* theTask)
The next figure illustrates these operations:
The result of the operations you have just carried out
We are now going to continue by defining the newly-created datatype as having public visibility. Modelio C++ Designer produces the following code for the “Task” class.
1//includes for used library types
2#include <string>
3#include <set>
4#include <list>
5
6//automatic includes (friends, associated classes, etc)
7#include "MyPlanner/TaskManagement/SimpleProject.h"
8#include "MyPlanner/TaskManagement/HumanResource.h"
9
10namespace MyPlanner
11{
12 namespace TaskManagement
13 {
14 class Task
15 {
16 //typedefs
17
18 public:
19 typedef bool (*TaskHandler)(Task* theTask);
20
21 //attributes
22
23 public:
24 std::string name;
25 std::string wbsCode;
26
27 //associations
28
29 protected:
30 SimpleProject* project;
31 std::set<Task> subTasks;
32 Task* owner;
33 std::list<HumanResource> resource;
34
35 //operations
36
37 public:
38 Task();
39 Task(const Task& value);
40 Task& operator =(const Task& value);
41 ~Task();
42 bool addSubTask(int wbsNum, Task& SubTask);
43 std::string getName();
44 void setName(std::string value);
45 std::string getWbsCode();
46 SimpleProject* getProject();
47 std::list<HumanResource>& getResource();
48 std::set<Task>& getSubTasks();
49 Task* getOwner();
50 void setOwner(Task* value);
51
52 //non-modeled members
53
54 };
55 }
56}
After this, we can easily create attributes of “TaskHandler” type. For example, let’s create the “handlers” attribute that models the mapping of handlers assigned to the given events for the task, and then create its model- level accessor. With the help of the accessor, we can assign our handlers to given events for a given task, and our code is concise due to the use of typedef.
The “handlers” attribute and the “getHandlers” accessor
Modelio C++ Designer produces the following code for the “getHandlers” accessor definition, where the typedef defined in the “Task” class is correctly referred to.
1//class header file
2#include "MyPlanner/TaskManagement/Task.h"
3
4namespace MyPlanner
5{
6 namespace TaskManagement
7 {
8 //...
9 std::hash_map<std::string,Task::TaskHandler>& Task::getHandlers()
10 {
11 //modifiable zone @16423@30671900:2486@T
12 return handlers;
13 //modifiable zone @16423@30671900:2486@E
14 }
15 }
16}
A type library groups together type definitions, which guide the translation of primitive types, data types and collection types used when modeling C++ implementations.
A type library can be selected on two levels, a project build targets or individual structural features or parameters.
To set a type library for a chosen model element, you simply select it in the library selector in the element’s C++ edition dialog box.
Setting a type library on the “TaskWindow” class
Modelio C++ Designer automatically uploads new type libraries and visualizes them in the library selector. The “Default” choice uses the current project’s type library for generation.
Let’s consider a more detailed model of our task management application. First we model the application GUI for the Windows platform, which uses the MFC library to implement GUI functionalities. We then design the “TaskWindow” class, which represents the window used to graphically visualize of task information (owner, subtasks, resources, and so on).
Let’s say that this class has the “Brush Resources” association with the “CBrush” class, which represents the display (GDI) brushes used to render tasks of various different statuses. To rapidly access brush resources, brush instances are associated with brush names.
The more detailed model of our task management application
We will now continue by selecting the “MFC” type library for the current project.
Selection of the “MFC” type library for the current project
As a result, Modelio C++ Designer produces the following code for the “TaskWindow” class. The “OrderedMap” semantics of the “browserResource” association end correspond to the “map” collection type provided by the MFC type library and are implemented by the “CMap” type. The include directives required to use this type are automatically generated.
1//includes for used library types
2#include <afxcoll.h>
3
4//automatic includes (friends, associated classes, etc)
5#include "MyPlanner/TaskManagement/Task.h"
6
7//modifiable zone @13936@30671900:989@T
8#include "afxwin.h"
9//modifiable zone @13936@30671900:989@E
10
11namespace MyPlanner
12{
13 namespace GUI
14 {
15 namespace Windows
16 {
17 class TaskWindow
18 {
19 //...
20 public:
21 TaskManagement::Task* task;
22 CMap<CString,CString&,CBrush,CBrush&> brushResource;
23
24 //operations
25
26 public:
27 TaskWindow();
28 TaskWindow(const TaskWindow& value);
29 TaskWindow& operator =(const TaskWindow& value);
30 ~TaskWindow();
31
32 //non-modeled members
33
34 };
35 }
36 }
37}
It is possible to specify type libraries up to particular structural features and parameters, thereby making it possible to have multiple type libraries defined for members of a single class.
Let’s continue by modeling the fact that the “TaskWindow” class implements the “ITaskView” interface, designed to support cross-platform GUI. This interface uses the “STL” type library due to STL portability.
The “ITaskView” interface defines the “formatDisplayTitle” operation used to format the task display title according to a given formatting string.
The latest developments we have made to our model
We are now going to create the string type “displayTitle” attribute in the “TaskWindow” class, which represents the task title rendered in the window. In this class, we will implement the “formatDisplayTitle” operation from the “ITaskView” interface.
If we simply generate the code, the “formatDisplayTitle” function defined in the “TaskWindow” class will not override the interface operation, because its string parameter type will be translated to the “CString” type, which implements it in the “MFC” type library.
To make the operation override the interface method, we will now specify the “STL” type library individually for its parameter.
Changing the library on the FormatStr parameter
Modelio C++ Designer now produces the following code for the “TaskWindow” class. Implementations of string types and include directives for their headers from both the “MFC” and “STL” type libraries are presented.
1//includes for used library types
2#include <cstringt.h>
3#include <string>
4#include <afxcoll.h>
5
6//automatic includes (friends, associated classes, etc)
7#include "MyPlanner/GUI/ITaskView.h"
8#include "MyPlanner/TaskManagement/Task.h"
9
10//modifiable zone @13936@30671900:989@T
11#include "afxwin.h"
12//modifiable zone @13936@30671900:989@E
13
14namespace MyPlanner
15{
16 namespace GUI
17 {
18 namespace Windows
19 {
20 class TaskWindow : public GUI::ITaskView
21 {
22 //...
23 private:
24 CString displayTitle;
25
26 //associations
27
28 public:
29 TaskManagement::Task* task;
30 CMap<CString,CString&,CBrush,CBrush&> brushResource;
31
32 //operations
33
34 public:
35 TaskWindow();
36 TaskWindow(const TaskWindow& value);
37 TaskWindow& operator =(const TaskWindow& value);
38 ~TaskWindow();
39 void formatDisplayTitle(std::string FormatStr);
40
41 //non-modeled members
42
43 };
44 }
45 }
46}
Note 1: You can also create your own type libraries using Modelio C++ Designer platform development features, by editing open ACT type library definitions. Type library selectors are fed dynamically, so when a new type library is installed, it is immediately visible and available for selection.
Note 2: When a type library is selected for a particular model element, this only suggests that it be used for type translation, but does not force Modelio C++ Designer to use only type implementations from this library. Therefore, when the type or collection type of a model element cannot be translated with a type library selected for it, the type library selected for its owner applies.
A type library can define non-primitive types, which are not semantically compatible with basic UML types, such as string or integer. To use such a type, create an empty datatype with the same name in the model, and then assign this datatype to the attribute or parameter type in the usual way. Modelio C++ Designer automatically substitutes it for the library definition, inducing include directives and applying ACTs specified there.
It can be useful to create these data types in a dedicated package specified not to produce the namespace and not to produce C++ code.
For example, the “MFC” type library provides the “CDC” type expressing the respective “MFC” class, which represents the device drawing context.
Create the “dc” attribute used to store the device context to draw the task window in the “TaskWindow” class. This attribute should have the “CDC” type.
It would not be wise to model the “CDC” class from MFC, because it defines a huge number of member functions. In order to use the CDC type defined in the “MFC” library, create the “CDC” datatype in the dedicated “Win32Types” package, marked as no code and no namespace.
The “CDC” datatype you have just created
Please note that the data type has been automatically decorated with the <<C++ DataType>> stereotype.
Continue by assigning the “CDC” datatype as being the type of the “dc” attribute in the “TaskWindow” class, by dragging it into the “Class” field in the standard attribute dialog box.
Modelio C++ Designer produces the following code for the “TaskWindow” class. Translation of the datatype to the “CDC” class and include directives required to use this class are presented.
1//includes for used library types
2#include <cstringt.h>
3#include <string>
4#include <afxwin.h>
5#include <afxcoll.h>
6
7//automatic includes (friends, associated classes, etc)
8#include "MyPlanner/GUI/ITaskView.h"
9#include "MyPlanner/TaskManagement/Task.h"
10
11namespace MyPlanner
12{
13 namespace GUI
14 {
15 namespace Windows
16 {
17 class TaskWindow : public GUI::ITaskView
18 {
19 //...
20 private:
21 CString displayTitle;
22 CDC dc;
23
24 //associations
25
26 public:
27 TaskManagement::Task* task;
28 CMap<CString,CString&,CBrush,CBrush&> brushResource;
29
30 //operations
31
32 public:
33 TaskWindow();
34 TaskWindow(const TaskWindow& value);
35 TaskWindow& operator =(const TaskWindow& value);
36 ~TaskWindow();
37 void formatDisplayTitle(std::string FormatStr);
38
39 //non-modeled members
40
41 };
42 }
43 }
44}