Ada_Code_GenerationAda Code Generation
The detailed rules used to generate Ada code from a Rhapsody model follow this section. But before giving the low-level rules, this section gives an overview of the generation concepts by showing simple examples of the Ada code that is generated from a particular model.
Contents
- Classes
- Attributes
- Operations
- Dependencies
- Actors
- Packages
- Types
- Template Classes and their instantiation
- Concurrent Types
- Entrypoints
- Singleton Classes
- Relations
- Unidirectional Relations
- Bidirectional Relations
- Custom Containers
- Ports
- Variation Points
- Static Generalization
- Pragma
- Ada Libraries
- Configuration of Main File Generation
- Instances Defined on a Package
- User-Defined header and footers
- Custom Makefiles
Classes
Class definition
A class in Rhapsody is represented as an Ada package, and produces a package specification and an optional package body in Ada. The name of the Ada package is the name of the class.
A simple class in Rhapsody.
--++ class class_name package class_name is type class_name_t; type class_name_acc_t is access all class_name_t; type class_name_wide_acc_t is access all class_name_t'class; type class_name_t is tagged null record; private end class_name;
The generated Ada code for a simple class.
The name of the generated file is class_name.ads .
In addition, three new types are declared in the public part of the package specification - a tagged record type, and two optional access to that type.
The access types can be generated in different way. Some properties control these types generation.
- Ada_CG::Class::RecordTypeName : this property controls the name of the tagged record type.
- Ada_CG::Class::AccessTypeName : this property controls the name of the class access type.
- Ada_CG::Class::ClassWideAccessTypeName : this property controls the name of the class wide access type.
- Ada_CG::Class::GenerateAccessType : the GenerateAccessType property determines which
access types are generated for the class. The possible values are as follows:
- None - Access types are not generated.
- Standard - An access type is generated.
type class_name_acc_t is access class_name_t;
- General - General access types are generated.
type class_name_acc_t is access all class_name_t;
- Ada_CG::Class::GenerateClassWideAccessType : This property determines which Wide access
types are generated for the class. The possible values are:
- None - wide access types are not generated
- Standard - a standard wide access type is generated
type class_name_wide_acc_t is access class_name_t'class;
- General - general wide access types are generated
type class_name_wide_acc_t is access all class_name_t'class;
- Constant - constant wide access types are generated
type class_name_wide_acc_t is access constant class_name_t'class;
Class record type visibility
The definition of this record type appears in the public part of the specification package where the record type is declared, but it could also appear in the private part by setting the “Ada_CG.Class.Visibility” property.
Controlling the location of the record type definition.
Inheritance
When a class inherits from another class, the record type for the subclass is an extension of the record type of the parent class.
Inheritance in Rhapsody.
with Superclass; --++ class Subclass package Subclass is type Subclass_t is new Superclass.Superclass_t with private; type Subclass_acc_t is access Subclass_t; --Public Fields/Variables accessors --------- function get_my_Boolean (this : in Subclass_t) return Boolean; pragma inline (get_my_Boolean); procedure set_my_Boolean (this : in Subclass_t; value : in Boolean); pragma inline (set_my_Boolean); private type Subclass_t is new Superclass.Superclass_t with record -- Fields -- my_Boolean : Boolean; --++ attribute my_Boolean end record; end Subclass;
The package specification for a specialized class.
Notice that a “With” statement has been generated for the superclass in the package specification of the subclass.
Initialization code
A non-abstract class can have initialization code that is executed during elaboration of the associated package. In order to generate such code, you’ll need to edit the Ada_CG::Class::InitializationCode property for that class.
Setting the initialization code property for a class
--++ class class_name package body class_name is --Functions/Procedures section --------------------- procedure myOperation (this : in out class_name_t) is begin null; --+[ operation myOperation() --+] end myOperation; begin null; end class_name;
Generated body for a class with initialization code
Static class
A static class is a class witch as only static attributes and operations. No tagged record type and no ”this” parameters are generated for this kind of class.
A Static class can be used for safety critical application.
setting IsStatic property for a class
abstract class and interface
Abstract class and Interface
A class can have the stereotype "abstract" or "interface". If Ada83 or Ada95 is selected, this will generate an abstract class record type. The class access type is generated in a different way than usual classes. It is a class wide access type by default.
type object_t; type object_acc_t is access all object_t'Class; type object_t is abstract tagged null record;
The class access type can be generated as usual, by setting the property Ada_CG::Operation::VirtualMethodGenerationScheme to ClassWideOperations. In this case, the property Ada_CG::Class::GenerateAccessType can be used as usual.
type object_t; type object_acc_t is access all object_t; type object_t is abstract tagged null record;
For Ada2005 and latest Ada version, "interface" stereotype generates an interface type. (See Ada2005 Code Generation section.)
Attributes
Accessor and mutator
By default, a mutator and accessor are created for each attribute.
Non-static attributes
When non-static attributes are added to a class, these attributes are added to the record type.
When non-static attributes are added to a package, these attributes are handled as static attributes.
Non-static attributes of a class.
--++ class class_name package class_name is type class_name_t; type class_name_acc_t is access all class_name_t; type class_name_t is tagged record -- Fields my_Boolean : Boolean; --++ attribute my_Boolean my_Integer : Integer; --++ attribute my_Integer end record; --Public Fields/Variables accessors --------------- function get_my_Boolean (this : in class_name_t) return Boolean; pragma inline (get_my_Boolean); procedure set_my_Boolean (this : in class_name_t; value : in Boolean); pragma inline (set_my_Boolean); function get_my_Integer (this : in class_name_t) return Integer; pragma inline (get_my_Integer); procedure set_my_Integer (this : in class_name_t; value : in Integer); pragma inline (set_my_Integer); private end class_name;
The package specification for non-static attributes.
Because accessor and mutator methods are created, a package body file is created with the name class_name.adb .
--++ class class_name package body class_name is --Fields/Variables accessors --------------- function get_my_Boolean (this : in class_name_t) return Boolean is begin return this.my_Boolean; end get_my_Boolean; procedure set_my_Boolean (this : in class_name_t; value : in Boolean) is begin this.my_Boolean := value; end set_my_Boolean; function get_my_Integer (this : in class_name_t) return Integer is begin return this.my_Integer; end get_my_Integer; procedure set_my_Integer (this : in class_name_t; value : in Integer) is begin this.my_Integer := value; end set_my_Integer; end class_name;
The package body for non-static attributes.
The record type now contains the two non-static attributes that were added in Rhapsody.
In addition, the attribute accessor and mutator operations contain a ‘this’ parameter that is used to pass in an instance of the type being affected. This is true for all non-static operations that are not for singleton classes.
The accessor and mutator are generated in the public part of the package specification by default, but they can be moved to private part by clicking the “Private” radio button on the features page.
Controlling the visibility of the accessor and mutator.
Static attributes
Attributes marked as static do not create record elements in classes, but instead are represented as variables in the Ada package.
Static attributes visibility
These variables can be defined in the public or private part of the package specification, or in the package body, depending on the setting of property “Ada_CG.Attribute.Visibility”.
Static attributes of a class.
In this example, both attributes are marked as static. However, the attribute privateStaticInt is marked as private, which means that the accessors will appear in the private part of the package specification, and its property “Ada_CG.Attribute.Visibility” is set to “Private”, forcing the variable definition to appear in the private part as well.
--++ class class_name package class_name is type class_name_t; type class_name_acc_t is access all class_name_t; type class_name_t is tagged null record; -- Public Variables/Constants --------------- public_Static_Boolean : Boolean; --++ attribute public_Static_Boolean --Public Fields/Variables accessors --------------- function get_public_Static_Boolean return Boolean; pragma inline (get_public_Static_Boolean); procedure set_public_Static_Boolean (value : in Boolean); pragma inline (set_public_Static_Boolean); private -- Private Variables/Constants --------------- private_Static_Integer : Integer; --++ attribute private_Static_Integer --Private Fields/Variables accessors --------------- function get_private_Static_Integer return Integer; pragma inline (get_private_Static_Integer); procedure set_private_Static_Integer (value : in Integer); pragma inline (set_private_Static_Integer); end class_name;
The package specification for static attributes.
--++ class class_name package body class_name is --Fields/Variables accessors --------------- function get_private_Static_Integer return Integer is begin return private_Static_Integer; end get_private_Static_Integer; procedure set_private_Static_Integer (value : in Integer) is begin private_Static_Integer := value; end set_private_Static_Integer; function get_public_Static_Boolean return Boolean is begin return public_Static_Boolean; end get_my_Boolean; procedure set_public_Static_Boolean (value : in Boolean) is begin public_Static_Boolean := value; end set_public_Static_Boolean; end class_name;
The package body for static attributes.
Static attributes declaration position
In Ada, declaration order is important. For example, a type declaration might depend on a constant that has to be declared before being used.
In order to provide some degree of control over the declaration order of attributes, the Ada_CG.Attribute.DeclarationPosition property can be used. The table below summarize the different values that this property can take and its effects on the attribute it is being applied.
Value | Description |
Default |
This is the default setting provided for compatibility reasons. It is similar to the AfterClassRecord setting with the following exception:
On new models, it is advised not to use this value. Should you change this value on previous models, make sure the code compiles once you’ve regenerated it. |
BeforeClassRecord | The attribute will be generated immediately before the class record |
AfterClassRecord | The attribute will be generated immediately after the class record |
StartOfDeclaration | The attribute will be generated immediately after the start of the section (public part of the specification, private part of the specification, package body) |
EndOfDeclaration | The attribute will be generated immediately before the end of the section (public part of the specification, private part of the specification, package body) |
Table 1 Ada_CG.Attribute.DeclarationPosition property values description
A few special cases:
- if the attributes have their Ada_CG.Attribute.Visibility property set to “Body”
- If the attributes are defined on a package
- If the Ada_CG.Class.Visibility property of the class they are defined in has a different setting
These attributes then have no actual class record around which they can be positioned. In such cases, they are generated around a “virtual” class record location that gives a declaration order as close as possible to the one that would exist if there was a class record definition in the section the attributes are being generated into.
Static attributes of a class with overridden declarationPosition property
Setting the DeclarationPosition for a static attribute to BeforeClassRecord
Setting the DeclarationPosition for a static attribute to AfterClassRecord
--++ class class_name package class_name is type class_name_t; type class_name_acc_t is access all class_name_t; -- Public Variables/Constants --------------- public_Static_Boolean_Before_Record : Boolean; --++ attribute public_Static_Boolean_Before_Record type class_name_t is tagged null record; -- Public Variables/Constants --------------- public_Static_Integer_After_Record : Integer; --++ attribute public_Static_Integer_After_Record --Public Fields/Variables accessors --------------- private end class_name;
Generated code for static attributes with overridden DeclarationPosition property
Non-Predefined Attribute Types
The attribute definitions can be entered directly in the “Ada declaration” field instead of choosing a predefined type. In this case, it might be necessary to set the two properties “Ada_CG.Attribute.AccessorGenerate” and “Ada_CG.Attribute.MutatorGenerate” to false so that the default accessors are not generated.
Non-static attribute definition.
Static attribute definition.
--++ class class_name package class_name is type class_name_t; type class_name_acc_t is access all class_name_t; type class_name_t is tagged record -- Fields -- -- This is a string attribute my_String : Integer := "testtest"; --++ attribute my_String end record; -- Public Variables/Constants --------------- -- This is a public string variable my_Static_String : String (1..8) := "stpublic"; --++ attribute my_Static_String --Public Fields/Variables accessors --------------- private end class_name;
The package specification for non-predefined type attributes.
Guarded Attributes
Access to attributes can be guarded by setting the “isGuarded” property. There are two possible settings. One setting is “all” which guards both the accessor and mutator of the attribute. The other setting is “mutator” which will only guard the mutator.
Guarding an Attribute.
When an attribute is guarded, a mutex is used to synchronize access to the attribute. Depending on the value of the “Ada_CG.<Class|Package>.UseAda83Framework property of the attribute owner, an Ada83 task based Mutex or an Ada95 protected object based Mutex will be used.
<<Discriminant>> Attributes
By setting a class instance attribute or a struct attribute stereotype to <<Discriminant>>, it is possible to generate a discriminated record type.
Modeling a class with a <<Discriminant>> attribute
Defining an unconstrained array type
Setting an attribute stereotype to <<Discriminant>>
Defining an attribute with a type definition based on the class record type discriminant
--++ class class_0 package class_0 is type class_0_t ( Size : Integer --++ attribute Size ); type class_0_acc_t is access all class_0_t; --Public types ------------------------------------- type Int_Array is array(Integer range <>) of Integer; type class_0_t ( Size : Integer --++ attribute Size )is tagged record -- Fields -- My_Int_Array : Int_Array(1..Size); --++ attribute My_Int_Array end record; private end class_0;
Generated code for a class with a discriminant
Note that no setter is generated for attributes with a <<Discriminant>> stereotype, no matter the setting of its Ada_CG.Attribute.MutatorGenerate property.
Overriding and redefining discriminant attributes
If the attribute is a <<Discriminant>> non-static attribute and it has an initial value and it is defined as a <<Discriminant>> attribute in at least one of its parent classes then the attribute may be generated as a constraint on the parent discriminant, as a new discriminant hiding the one from the parent or as both.
This behavior is controlled by using the “Ada_CG.Attribute.RedefiningDiscriminantPolicy “ and “Ada_CG.Attribute.ParentDiscriminantValue” properties.
Class with overriding and redefining discriminant
Overriding discriminant
Overriding and redefining discriminant
Redefining discriminant
With Base_Class; --++ Class Derived_Class package Derived_Class is type Derived_Class_t ( overridden_and_redefined : Integer; --++ attribute overridden_and_redefined redefined : Integer --++ attribute redefined ); type Derived_Class_acc_t is access all Derived_Class_t; type Derived_Class_t ( overridden_and_redefined : Integer; --++ attribute overridden_and_redefined redefined : Integer --++ attribute redefined ) is new Base_Class.Base_Class_t( overridden => 10, --++ attribute overridden overridden_and_redefined => 100 --++ attribute overridden_and_redefined )With null record; private end Derived_Class;
Deferred constant
The deferred constant is defined in the private part of a package. And a partial declaration is also made in public part. In order to generate a deferred constant, several conditions must be matched.
- The attribute must be constant.
- If the attribute belongs to a class, it must be static.
- The attribute must have a default value.
- The property Ada_CG::Attribute::DeferredInitializationPosition must be different than "None".
- The property Ada_CG::Attribute::DeclarationPosition must be set with the same value than the property Ada_CG::Attribute::DeferredInitializationPosition.
--++ package Default::Complex_Numbers package Complex_Numbers is --Public types ------------------------------- --++ Type Forward_Complex type Complex is private; -- Public Variables/Constants ---------------- I : constant Complex; private --Private types ------------------------------ --++ Type Complex type Complex is record -- Fields -- Rl : Float; --++ attribute Rl Im : Float; --++ attribute Im end record; -- Public Deferred Constants Initialization - I : constant Complex := (0.0, 1.0); --++ attribute I end Complex_Numbers;
Operations
Operations created in Rhapsody will result in Ada functions if there is a return type, or procedures if there is not. In this example, myOperation will be a function with two parameters that returns an Integer. And myProc will be a procedure with one parameter.
Operations defined on a class.
Operation Features
The implementation of myOperation .
The local variables for myOperation .
--++ Class class_name package class_name is type class_name_t; type class_name_acc_t is access all class_name_t; type class_name_t is tagged null record; --Public Functions/Procedures section -------- --++ operation myOperation(Integer, Boolean) function myOperation (this : in class_name_t; arg1 : in Integer; arg2 : in Boolean ) return Integer; --++ operation myProc(Integer) procedure myProc ( arg1 : in Integer ); private end class_name;
Operations in the package specification.
--++ Class class_name package body class_name is --Functions/Procedures section -------- function myOperation (this : in class_name_t; arg1 : in Integer; arg2 : in Boolean ) return Integer is myLocal : Boolean := TRUE; begin --+[ operation myOperation(Integer, Boolean) return 5; --+] end myOperation; procedure myProc ( arg1 : in Integer ) is begin null; --+[ operation myProc(Integer) --+] end myProc; end class_name;
Operations in the package body.
In the operation bodies, the implementation provided in Rhapsody has been used for myOperation , but an appropriate default statement has been created for myProc because the implementation field in Rhapsody has been left blank. Any lines entered in this implementation field will replace this default statement.
Guarded operations
An operation can be made guarded by setting the “Concurrency” property to guarded .
Making an Operation Guarded.
When an operation is guarded, a mutex is used to synchronize access to the operation. Depending on the value of the “Ada_CG.<Class|Package>.UseAda83Framework property of the operation owner, an Ada83 task based Mutex or an Ada95 protected object based Mutex will be used.
Template operations and their instantiations
The mechanism for supporting template operations and their instantiations is very similar to the one available for template classes and their instantiation.
Modeling a template operation and a template operation instantiation
features of a template operation
setting up template parameters for a template operation
--++ package Generic_Op_Pkg package Generic_Op_Pkg is --Public Functions/Procedures section ------- --++ operation Generic_Function(Integer) generic gen_param : Integer; function Generic_Function ( regular_param : in Integer ) return Integer; private end Generic_Op_Pkg;
generated code for a template operation specification
--++ package Generic_Op_Pkg package body Generic_Op_Pkg is --Functions/Procedures section ------- function Generic_Function ( regular_param : in Integer ) return Integer is --+[ operation myOperation(Integer, Boolean).Variables --+] begin return 0; --+[ operation myOperation(Integer, Boolean) --+] end Generic_Function; end Generic_Op_Pkg;
generated code for a template operation implementation
features of a template operation instantiation
setting up template arguments for a template operation instantiation
--++ package Generic_Op_Instantiation_Pkg package Generic_Op_Instantiation_Pkg is --Public Functions/Procedures section ------- --++ operation Generic_Function_Instantiation(Integer) function Generic_Function_Instantiation is new Generic_Op_Pkg.Generic_Function( gen_param => 8 ); private end Generic_Op_Instantiation_Pkg;
generated code for a template operation instantiation
Access parameters
Ada95 introduced the concept of access parameters. In order to set the mode of a parameter to be “access”, as opposed to “in”, “out”, or “in out”, first the package in which is defined the operation has to generate Ada95 code (and not Ada83), second you will need to edit the properties of the parameter to set the AsAccess property to true.
Making a parameter passing mode "access"
You can also choose to pass the this parameter as an access mode parameter for a non-static operation, to do this you need to edit the properties of the operation to set the ThisByAccess property to true.
Making an operation this parameter passing mode "access"
type class_name_t; type class_name_acc_t is access all class_name_t; type class_name_t is tagged null record; --Public Functions/Procedures section ------- --++ operation myOperation(Integer, Boolean) function myOperation (this : access class_name_t; arg1 : access Integer; arg2 : in Boolean ) return Integer;
Operation using access mode parameters
Class-wide parameters
In order to specify whether a parameter is to be passed class-wide or not, you will need to set its “ClassWide” property to true.
Dependencies
<<Usage>> dependencies
Dependencies stereotyped as <<Usage>> in Rhapsody create “With” statements in the generated Ada packages. If the “Ada_CG.Dependency.CreateUseStatement” property is set to “Use”, a “Use” statement will also be created for the target package.If it is set to “UseType”, a “Use Type” statement will also be created for the target type. By default, the “With” and “Use” statements will appear in the package specification. They can be moved to the package body by setting the “CG.Dependency.UsageType” property to “Implementation”.
<<Usage>> dependencies in Rhapsody Developer for Ada.
An implementation dependency.
Creating a "Use" statement.
With actor_3; With class_0; With package_0; Use class_0; --++ class class_1 package class_1 is type class_1_t; type class_1_acc_t is access all class_1_t; type class_1_t is tagged null record; --Public Functions/Procedures section ------- --++ operation Message_0() function message_0 (this : in out class_1_t); private end class_1;
The package specification for dependencies.
With package_0.class_2; --++ class class_1 package body class_1 is type class_1_t; type class_1_acc_t is access all class_1_t; type class_1_t is tagged null record; --Functions/Procedures section ------- function message_0 (this : in out class_1_t) is begin null; --+[ operation Message_0() --+] end message_0; end class_1;
The package body for dependencies.
Note that elaboration pragmas can be generated for the supplier class or package of the dependency in the client class or package by setting the appropriate properties on the dependency:
- Ada_CG.Dependency.GeneratePragmaElaborate
- Ada_CG.Dependency.GeneratePragmaElaborateAll
<<Renames>> dependencies
Dependencies stereotyped as <<Renames> in Rhapsody create “renames” statements in the generated Ada packages.
Valid <<Renames>> dependencies can be modelized between any two model elements of the same kind among the following ones:
- Packages
- Classes
- Operations
- Attributes
-
- Defined on a package
- Defined on a class with a static modifier
Be aware that using this feature on classes limits what you can do with the renaming class. More specifically :
- You cannot derive other classes from
- Adding attributes or operations to it has no effect on the generated code
Note that for operations:
- Signatures have to be compatible
- It is possible to have a “renaming as spec” or a “renaming as body” behavior depending on the setting of the “CG.Dependency.UsageType” property (Specification or Implementation).
Only <<renames>> dependencies between classes and packages can be drawn on the Object Model Diagrams of Rhapsody. In order to model <<renames>> dependencies between two attributes or two operations, one has to use the context menu in the Rhapsody browser.
Actors
Actors generate exactly the same code as classes.
An Actor in Rhapsody.
With Class_0; With Super_Actor; --++ actor Actor_0 package package_1.Actor_0 is type Actor_0_t; type Actor_0_acc_t is access all Actor_0_t; type Actor_0_t is new Super_Actor.Super_Actor_t with record -- Fields -- attr_1 : Integer; --++ attribute attr_1 end record; --Public Variables/Constants ----------------- private_static_attr : Integer; --++ attribute private_static_attr --Public Functions/Procedures section ------- --++ operation operation() procedure operation (this : in out Actor_0_t); --++ operation static_operation() procedure static_operation; --Public Fields/Variables accessors ----------- function get_attr_1 (this : in Actor_0_t) return Integer; pragma inline (get_attr_1); procedure set_attr_1 (this : in Actor_0_t; value : in Integer); pragma inline (set_attr_1); private --Private Fields/Variables accessors ----------- function get_private_static_attr (this : in Actor_0_t) return Integer; pragma inline (get_private_static_attr); procedure set_private_static_attr (this : in Actor_0_t; value : in Integer); pragma inline (set_private_static_attr); end package_1.Actor_0;
Package specification for an Actor.
--++ actor Actor_0 package body package_1.Actor_0 is --Functions/Procedures section ------- procedure operation (this : in out Actor_0_t) is --+[ operation operation().Variables --+] begin null; --+[ operation operation() --+] end operation; --++ operation static_operation() procedure static_operation is --+[ operation static_operation().Variables --+] begin null; --+[ operation static_operation() --+] end static_operation; --Fields/Variables accessors ----------- function get_attr_1 (this : in Actor_0_t) return Integer is begin return this.attr_1; end get_attr_1; procedure set_attr_1 (this : in Actor_0_t; value : in Integer) is begin this.attr_1 := value; end set_attr_1; function get_private_static_attr (this : in Actor_0_t) return Integer is begin return this.private_static_attr; end get_private_static_attr; procedure set_private_static_attr (this : in Actor_0_t; value : in Integer) is begin this.private_static_attr := value; end set_private_static_attr; end package_1.Actor_0;
Package body for an Actor.
Packages
Like classes, packages in Rhapsody will also be represented as Ada packages. A package in Rhapsody can have functions and variables, which will be handled in the same manner as static operations and static attributes on a class. A package can also have initialization code.
In this example, the package specification will be named “myPackage.ads” and the package body will be “myPackage.adb”.
A package defined in Rhapsody.
--++ package Default::my_Package package my_Package is --Public Variables/Constants ----------------- my_Integer : Integer; --++ attribute my_Integer my_Boolean : Boolean; --++ attribute my_Boolean --Public Functions/Procedures section ------- --++ operation my_Function(Integer) function my_Function ( argument_0 : in Integer ) return Boolean; --++ operation my_Procedure() procedure my_Procedure; --Public Fields/Variables accessors --------- function get_my_Integer return Integer; pragma inline (get_my_Integer); procedure set_my_Integer (value : in Integer); pragma inline (set_my_Integer); function get_my_Boolean return Boolean; pragma inline (get_my_Boolean); procedure set_my_Boolean (value : in Boolean); pragma inline (set_my_Boolean); private end my_Package;
The package specification for a Rhapsody package.
--++ package Default::my_Package package body my_Package is --Functions/Procedures section --------------- --++ operation my_Function(Integer) function my_Function ( argument_0 : in Integer ) return Boolean is --+[ operation my_Function(Integer).Variables --+] begin return true; --+[ operation my_Function(Integer) --+] end my_Function; --++ operation my_Procedure() procedure my_Procedure is --+[ operation my_Procedure().Variables --+] begin null; --+[ operation my_Procedure() --+] end my_Procedure; --Public Fields/Variables accessors --------- function get_my_Integer return Integer is begin return my_Integer; end get_my_Integer; procedure set_my_Integer (value : in Integer) is begin my_Integer := value; end set_my_Integer; function get_my_Boolean return Boolean is begin return my_Boolean; end get_my_Boolean; procedure set_my_Boolean (value : in Boolean) is begin my_Boolean := value; end set_my_Boolean; end my_Package;
The package body for a Rhapsody package.
Child Packages
When working with Ada 95, classes and packages are also used as the namespace for classes and packages contained within them. This containment is used to create child packages. In Ada 83, this containment does not have any effect.
Packages and classes used as namespaces.
--++ class class_1::class_2 package my_Package.my_Sub_Package.class_1.class_2 is type class_2_t; type class_2_acc_t is access all class_2_t; type class_2_t is tagged null record; private end my_Package.my_Sub_Package.class_1.class_2;
The package specification for class_2.
The resulting files including the namespaces.
In this example, the Ada package name is prefixed by the parent names. For example, class_2 is contained in class_1 , which is found in my_Sub_Package , which itself if located inside of my_Package . Therefore the package name for class_2 is my_Package.my_Sub_Package.class_1.class_2 . This package will be found in the configuration directory in my_Package/my_Sub_Package/class_1 .
Also notice that class_0 is located in the “Default” package, and therefore is not considered as a child package.
Nested Packages
Child packages are in the namespace of their parent, but they are defined in separate files. Nested packages are not only in the namespace of their parent, but they are also defined in the same files as their parent.
Example of a nested package and a nested class.
In order to generate nested packages, one has to set the Ada_CG.Package.IsNested property for a package or the Ada_CG.Class.IsNested property for a class.
Setting a class to be generated as a nested package.
Setting a package to be generated as a nested package.
To determine in which section of the parent package the specification of the nested package will be generated, one can use the Ada_CG.Package.NestingVisibility property for a package, or the Ada_CG.Class.NestingVisibility property for a class.
Controlling the location of the specification of a nested package
Note that any package or class defined inside of a package or class that is itself nested will be generated as a nested package too.
Private Packages
Packages and classes can be defined as private via the use of the Ada_CG.Package.IsPrivate property for packages and Ada_CG.Class.IsPrivate property for classes.
Example of a private package and a private class.
Setting a class to be generated as a private package
Setting a package to be generated as a private package
--++ class Private_Child_Class private package Parent_Pkg.Private_Child_Class is type Private_Child_Class_t; type Private_Child_Class_acc_t is access all Private_Child_Class_t; type Private_Child_Class_t is tagged null record; private end Parent_Pkg.Private_Child_Class;
Specification of a private class
--++ package Parent_Pkg::Private_Child_Package private package Parent_Pkg.Private_Child_Package is private end Parent_Pkg.Private_Child_Package;
Specification of a private package
Note that a nested class or package cannot be private.
Elaboration Pragmas
Via the use of appropriate tags, different pragmas can be generated for a class or a package
Example of a class and a package with elaboration pragmas
Enabling generation of elaboration pragmas on a class
Enabling generation of elaboration pragmas on a package
--++ class Class_With_Elaboration_Pragmas package Class_With_Elaboration_Pragmas is pragma elaborate_body; pragma preelaborate; pragma pure; type Class_With_Elaboration_Pragmas_t; type Class_With_Elaboration_Pragmas_acc_t is access all Class_With_Elaboration_Pragmas_t; type Class_With_Elaboration_Pragmas_t is tagged null record; private end Class_With_Elaboration_Pragmas;
Specification of a class with elaboration pragmas
--++ package Default::Pkg_With_Elaboration_Pragmas package Pkg_With_Elaboration_Pragmas is pragma elaborate_body; pragma preelaborate; pragma pure; private end Pkg_With_Elaboration_Pragmas;
Specification of a package with elaboration pragmas
Please read the section on <<Usage>> dependencies to see how to generate “elaborate” and “elaborate_all” pragmas.
Note that other pragmas can be generated for a class or a package via the use of the following properties:
- For a class
-
- Ada_CG.Class.SpecificationPragmas
- Ada_CG.Class.SpecificationPragmasInContextClause
- Ada_CG.Class.ImplementationPragmas
- Ada_CG.Class.ImplementationPragmasInContextClause
- For a package
-
- Ada_CG.Package.SpecificationPragmas
- Ada_CG.Package.SpecificationPragmasInContextClause
- Ada_CG.Package.ImplementationPragmas
- Ada_CG.Package.ImplementationPragmasInContextClause
<<Container>> Packages
Any class or package that is defined within a package stereotyped as <<Container>> will not include the package in its namespace.
In the following example, Package_1 is stereotyped <<Container>>.
A Sample <<Container>> Package.
In this example, every package is generated, but the namespaces for Class_A, Package_2, and Class_B do not include Package_1.
Types
Type declaration
Types can be created in either packages or classes.
Types defined in Rhapsody.
The definition of the type is taken in different way.
- language
- Enum
- Structure
- Typedef
Language Type declaration
The definition of the type is taken from the “Ada declaration” field,. In the declaration, a “%s” can be inserted to represent the name of the type.
The declaration of a private type on a class.
The declaration of a public type on a class.
The declaration of a private type on a package.
The declaration of a public type on a package.
Type visibility
A type definition can appear in the public or private portion of the resulting package specification, or in the package body.
Controlling the visibility of a type.
--++ class class_1 package class_1 is type class_1_t; type class_1_acc_t is access all class_1_t; --Public types ------------------------ -- This is the declaration of my type. type my_Type is new Integer range 0..5; type class_1_t is tagged null record; private --Private types ------------------------ -- Definition of my private type. subtype my_Private_Type is Integer range 1..5; end class_1;
The package specification for a class with types.
--++ package package_1 package package_1 is --Public types ------------------------ -- The declaration of the public type. type public_Type is (one, two, three); private --Private types ------------------------ -- Declaration of the private type. type private_Type is new Positive; end package_1;
The package specification for a package with types.
Type declaration position
In order to provide some degree of control over the declaration order of types, the Ada_CG.Type.DeclarationPosition property can be used. Its behavior is very similar to the one of the Ada_CG.Attribute.DeclarationPosition property for attributes, with the following exceptions:
- There is no “default” value for this property on types
- If an attribute and a type have a similar value for their respective declarationPosition properties, then the attribute will be generated before the type declaration.
Typedef type declaration
The typedef declaration will generate a new kind of type
Type Type_0 is new Boolean;
If the "Reference" box is checked, an access type is created
Type Type_0 is access Boolean;
The property Ada_CG.Type.AccessKind allow refining the the kind of access type
Type Type_0 is access all Boolean; -- Ada_CG.Type.AccessKind = General Type Type_1 is access constant Boolean; -- Ada_CG.Type.AccessKind = Constant
Type defined as a class
A type can be also defined with a class with a stereotype. This enables to have more visibility on type relations. The stereotypes are defined in the profile AdaCodeGenerator.
Main stereotype is “Type”. It is applicable on a class. This stereotype has a tag “IsSubtype”. If this tag is set to true, then the class will define a subtype.
Any type has a reference to another Ada type. This reference is represented by a dependency with the stereotype “New”. The dependency can be set to a Rhapsody type or a Rhapsody typed class.
Basic use cases:
Definition of a type
Representation of a typed class
-- Public Types -------------------------- type Integer_32 is new Integer;
Definition of a subtype
Representation of a subtype
The generated code of a subtype is:
-- Public Types -------------------------- subtype A_SubType is Integer;
Some other stereotypes are derived from “Type”
- RangeType : allow defining range type
- ArrayType : allow defining array type
- VariantRecordType : allow defining variant record type
Range type
A range type is defined by a class which has the stereotype “RangeType”.
The range is defined by two different ways.
- First it can be defined in a free text box in the tag “rangeDefinition”.
- It can also be defined with dependencies to a constant of the model. The dependencies have the stereotype “highRangeValue” or “lowRangeValue”.
Basic use cases:
Case I
- “New” dependency to a predefined type
- rangeDefinition set to “1..10”
Representation of a range type
The generated code of a range type is:
-- Public Types -------------------------- type A_Range_Type is new Integer range 1..10;
Case II
- “New” dependency to a typed class
- “HighRangeValue” dependency to a constant of the model
- “IsSubtype” tag set to true
- “rangeDefinition” tag set to “1”
Representation of a range type with dependency to a constant
The generated code of a range type is:
-- Public Types -------------------------- subtype A_Range_Type_1 is Default.Integer_32_2 range 1..Default.variable_11;
Array type
An array type is defined by a class which has the stereotype “ArrayType”.
The size of the array is defined in a free text box of the tag “Size”
Representation of an array type
The generated code of an array type is:
-- Public Types -------------------------- type An_Array_Type is array (1..10) of Integer;
Variant record type
A variant record type is defined by a class which has the stereotype “VariantRecordType”.
This class has several elements which describe the structure of this record.
Representation of a variant record type
The class has some attributes which represent the attributes of the record type.
Some attributes have the stereotype discriminant. Those are discriminant attributes of the record type.
The class has a part with stereotype “Case”. It represents the variant part of the record. The name of this part must be the name of the discriminant attribute used in the variant part.
The “Case” part has as many “When” parts as “When” cases in the Ada variant part. Those “When” parts have attributes, and a tag “Condition”, which defines the value of the “when” condition.
The generated code of a variant recod type is:
-- Public Types -------------------------- type A_Variant_Record_Type ( attribute_00 : Default.A_Enum; attribute_01 : Default.A_Range_Type; ) is record -- fields -- attribute_1 : Integer; attribute_2 : Integer; case attribute_00 is when AA => attribute_1_of_when_condition : Integer; when BB..CC => attribute_2_of_when_condition : Integer; attribute_3 : Integer; end case; end record;
Representation clause
A representation clause can be added on any type by updating the tag representationClauses.
Representation clause definition.
-- Public Types -------------------------- type RR is record -- Fields -- Code : Opcode; --++ attribute Code R1 : Register; --++ attribute R1 R2 : Register; --++ attribute R2 end record; for RR'Alignement use 2; for RR use record Code at 0 range 0 .. 7; R1 at 1 range 0 .. 3; R2 at 1 range 4 .. 7; end record;
Template Classes and their instantiation
template classes
Creating a template class in Rhapsody results in the generation of a generic Ada package. The arguments become the generic parameters.
In this example, arg1 is an Integer argument and arg2 is a Boolean argument.
Definition of a template class.
--++ class generic_Class generic arg1 : Integer; arg2 : Boolean; package generic_Class is type generic_Class_t; type generic_Class_acc_t is access all generic_Class_t; type generic_Class_t is tagged null record; private end generic_Class;
Package specification for a generic package.
template instantiations
An instantiation of this generic package is created by using an instantiation class in Rhapsody.
An instantiation of a template class.
The generated result is an instantiation of the generic package using the supplied arguments.
with generic_Class; --++ class generic_Instantiation package generic_Instantiation is new generic_Class(arg1 => 5, arg2 => true);
The generated Ada package for a generic instantiation.
template inheritance
Note that it is possible to have a template class inherit from another template class.
Inheritance between template classes
With generic_Parent; --++ class generic_Child generic with package generic_Parent_Instantiation is new generic_Parent(<>); arg_3 : Character; package generic_Child is type generic_Child_t; type generic_Child_acc_t is access all generic_Child_t; type generic_Child_t is new generic_Parent_Instantiation.generic_Parent_t with null record; private end generic_Child;
generated code for a template class derived from another template class
template instantiation inheritance
In order to fully benefit from the facilities offered by template inheritance, an efficient way to instantiate the whole class hierarchy is needed.
Modeling instantiation of a template inheritance hierarchy
Note that to specify that a derived class instantiation depends on a parent class instantiation, we use a <<Parent_Instantiation>> dependency from the derived class instantiation to the base class instantiation. This approach allows for reusing of the same parent class instantiation by several derived class instantiations
With generic_Parent; --++ class generic_Parent_Instantiation package generic_Parent_Instantiation is new generic_Parent(arg1 => 5, arg2 => true);
generated code for a base template instantiation class
With generic_Child; With generic_Parent_Instantiation; --++ class generic_Child_Instantiation_1 package generic_Child_Instantiation_1 is new generic_Child( generic_Parent_Instantiation => generic_Parent_Instantiation, arg3 => 'a' );
generated code for a derived template instantiation class
With generic_Child; With generic_Parent_Instantiation; --++ class generic_Child_Instantiation_2 package generic_Child_Instantiation_2 is new generic_Child( generic_Parent_Instantiation => generic_Parent_Instantiation, arg3 => 'b' );
generate code for another derived template instantiation class
Note that if the derived template class is an (Ada) child package of the base class, the generated code will slightly differ to accommodate the special visibility that the child has upon its parent
Modeling template inheritance hierarchy across (Ada) children packages
--++ class generic_Parent::generic_Child generic arg3 : Character; package generic_Parent.generic_Child is type generic_Child_t; type generic_Child_t is access all generic_Child_t; type generic_Child_t is new generic_Parent.generic_Parent_t with null record; private end generic_Parent.generic_Child;
Generated code for a derived template class that is a child package of its base class
With generic_Parent.generic_Child; With generic_Parent_Instantiation; --++ class generic_Child_Instantiation_1 package generic_Child_Instantiation_1 is new generic_Parent_Instantiation.generic_Child( arg3 => 'a' );
Generated code for the instantiation of a derived template class that is a child package of its base class
Concurrent Types
Tasks
Starting with Rhapsody Developer for Ada rev 8.1.4, the ada task representation changes. Before, two stereotypes were used to create ada task: <<AdaTask>> and <<AdaTaskType>> .
Now these stereotypes are deprecated and have been renamed <<D_AdaTask>> and <<D_AdaTaskType>>. The old names are used for another purpose.
Two new stereotypes, <<AdaSingleTask>> and <<AdaTaskType>>, have been created in order to be able to create Ada task types or Ada single tasks inside classes.
The legacy task representation is style available and is explained in the first part. A second part will detail how we can represent simple Ada task type and Ada single task in a Rhapsody model.
Legacy Tasks
Tasks in RiA are represented by a class with stereotype D_AdaTask or D_AdaTaskType. In this case, an Ada package is generated, with a tagged record type to represent a usual UML class. An Ada task is generated in this Ada package, and additional operations or attributes are also generated to manage the task.
A class with D_AdaTask stereotype is a usual class which will contain an Ada single task declared in specification file, and a task body in the body file. The effect of this construction, is that the Ada task will be created only once, when the Ada package will be executed.
package Ada_task_class is type Ada_task_class_t; type Ada_task_class_acc_t is access all Ada_task_class_t; --task type declaration task Ada_task_class_task is end Ada_task_class_task; type Ada_task_class_t is tagged null record; private end Ada_task_class;
A class with D_AdaTaskType stereotype is a usual class, which will contain an Ada task type declared in specification file, and a task body in body file. A task attribute will also be generated in class record type. The initialize function will instantiate this task attribute. In this case, at run time, the task will be created only when the Rhapsody class will be instantiated and initialized. Several instance of the task will run if several instance of the class are created.
package Ada_Task_Type_Class is type Ada_Task_Type_Class_t; type Ada_Task_Type_Class_acc_t is access all Ada_Task_Type_Class_t; --task type declaration task type Ada_Task_Type_Class_task is end Ada_Task_Type_Class_task; type Ada_Task_Type_Class_task_acc is access Ada_Task_Type_Class_task; type Ada_Task_Type_Class_t is tagged record my_Ada_Task_Type_Class_task : Ada_Task_Type_Class_task_acc; end record; procedure Initialize (this : in out Ada_Task_Type_Class_t); procedure Finalize (this : in out Ada_Task_Type_Class_t); private end Ada_Task_Type_Class;
Task entries
All operations defined in this class will be considered as task entries. It is possible to add an entry stereotype on these operations. This will not change operation default behavior.
An operation will generate the following code
- Entry in task declaration.
- Class operation which will call the entry of the task.
- Populate the task body.
type Ada_Task_Type_Class_t; type Ada_Task_Type_Class_acc_t is access all Ada_Task_Type_Class_t; task type Ada_Task_Type_Class_task is entry My_Entry_1 (this : in out Ada_Task_Type_Class_t); entry My_Entry_2 (this : in out Ada_Task_Type_Class_t); end Ada_Task_Type_Class_task; type Ada_Task_Type_Class_task_acc is access Ada_Task_Type_Class_task; type Ada_Task_Type_Class_t is tagged record my_Ada_Task_Type_Class_task : Ada_Task_Type_Class_task_acc; end record; procedure My_Entry_1 (this : in out Ada_Task_Type_Class_t); procedure My_Entry_2 (this : in out Ada_Task_Type_Class_t);
The following code shows how the entries operations will be called from the Ada_Task_Class class.
procedure My_Entry_1 (this : in out Ada_Task_Type_Class_t) is begin this.my_Ada_Task_Type_Class_task.My_Entry_1 (this); end My_Entry_1; procedure My_Entry_2 (this : in out Ada_Task_Type_Class_t) is begin this.my_Ada_Task_Type_Class_task.My_Entry_2 (this); end My_Entry_2;
Regular operation
If a user wants to generate an operation which does not represent an entry, then the Ada_CG::Operation::isEntry property must be set to false.
Pragmas
It is possible to generate some pragmas on tasks specification, by using some properties:
- CG.Class.ActiveThreadPriority
pragma Priority(XXX)
--task type declaration task type task_with_priority is pragma Priority(5); end task_with_priority
- CG.Class.ActiveStackSize
pragma Storage_Size(XXX)
--task type declaration task type task_with_storage_size is pragma Storage_Size(64); end task_with_storage_size
Task body
The task body will be automatically populated with the user defined entries. The skeleton of the task body contains an endless loop with a select statement. Each entry operation of the model will define an accept statement in the select statement. The implementation of accept statement is defined in operation implementation.
This is the default code generator behavior.
task body Ada_Task_Type_Class_task is begin loop select accept my_entry_1 do -- operation implementation here: . . . end my_entry_1; or accept my_entry_2 do -- operation implementation here: . . . end my_entry_2; end select; end loop; end Ada_Task_Type_Class_task;
It is possible to customize this template. In this template, you can add:
- Entry condition
- Synchronous execution request
- Task default action
Entry condition
Guard can be set on each accept statement. It can be done with the Ada_CG::Operation::EntryCondition property of the entry operation. This property should contain the condition which must be met before executing the select statement.
task body Ada_Task_Type_Class_task is begin loop select -- property Ada_CG::Operation::EntryCondition contains -- A = 1 and b = 2 when A = 1 and b = 2 => accept my_entry_1 do end my_entry_1; or accept my_entry_2 do end my_entry_2; end select; end loop; end Ada_Task_Type_Class_task;
Synchronous execution request
Operations can have either a HSER or LSER stereotype to indicate highly synchronous or loosely synchronous execution requests respectively. If no stereotypes are set on the operations, they are considered as HSRE by default.
If LSRE stereotype is set, the following code is generated
task body Ada_Task_Type_Class_task is begin loop select -- The my_entry_1 operation has LSER stereotype; accept my_entry_1; -- Here is the place of my_entry_1 operation -- implementation or -- The my_entry_2 operation has HSER stereotype; accept my_entry_2 do -- Here is the place of my_entry_2 operation -- implementation end my_entry_2; end select; end loop; end Ada_Task_Type_Class_task;
Task default action
An alternative operation can be set at the end of the select statements. The Ada_CG::Operation::TaskDefaultScheme property of the class (not of the operation) must be set to conditional or timed.
The alternative operation should have the stereotype TaskDefaultAction.
Ada_CG::Operation::TaskDefaultScheme property set to conditional
task body Ada_Task_Type_Class_task is begin loop select accept my_entry_1; or accept my_entry_2 do end my_entry_2; else -- The Alt_Op has TaskDefaultAction stereotype implementation of Alt_Op end select; end loop; end Ada_Task_Type_Class_task;
Ada_CG::Operation::TaskDefaultScheme property set to timed
In this case a delay statement can be generated just before task default implementation. It is defined in Ada_CG::Operation::TaskDefaultSchemeDelayStatement property.
task body Ada_Task_Type_Class_task is begin loop select accept my_entry_1; or accept my_entry_2 do end my_entry_2; or -- Ada_CG::Operation::TaskDefaultSchemeDelayStatement -- property is set to: delay 1.0; Delay 1.0; -- -- The Alt_Op has TaskDefaultAction stereotype implementation of Alt_Op end select; end loop; end Ada_Task_Type_Class_task;
User defined task body
Finally, if this template cannot match with user needs, then a last property exists in order to define manually all task body implementation.
If the Ada_CG::Class::TaskBody property is not empty, then all this template is replaced by the content of this property. It should contain the whole declaration of task body
-- task body is defined manually into Ada_CG::Class::TaskBody property task body User_Defined_Ada_Task_Body_task is begin -- here is the implementation defined by the user . . . end User_Defined_Ada_Task_Body_task;
New Ada Tasks representation
This second part describes a new representation of Ada tasks in Rhapsody model.
user defined Task
User can define as many single tasks or task types as needed inside a class. The whole structure of the class and the tasks is defined in the model without using any properties. User is free to define task entries, task body structure, task pragmas or task type discriminants, by adding corresponding Rhapsody element in the task model.
A Task type is represented by a nested class which has stereotype AdaTaskType. This will generate a task type inside the class or package which contains this AdaTaskType class.
A single Task is represented by an implicit part or object which has the stereotype AdaSingleTask. This will generate a single task inside the class or the package which contains this AdaSingleTask part or object.
The name of AdaTaskType class or AdaSingleTask object will give the name of the Ada task.
In the sample model shown below, the "class_with_tasks" class contains two tasks:
- Single_Task: this is a Ada single task. It has a task body an My_Entry_1 entry.
- Task_1: this is a task type. It has a task body and two entries.
The class has also two parts which define two instances of the Ada task type.
Task declaration
The declaration of the tasks is
task single_task is entry My_Entry_1 ; entry My_Entry_2(aa : integer); end single_task; task type Task_1 is entry My_Entry_1; end Task_1; task_1_acc is access task_1;
Entries
Entries are defined with some usual operations.
Task body and task entries
The task body is represented by an operation with the stereotype TaskBody. This operation is mandatory. This operation is automatically created in the task type. Only one operation of this kind can be created. Only implementation of the operation will be used to define the task body.
In order to use user defined entries in task body, some macro can be added inside the TaskBody operation implementation in order to copy the code of entries. TaskBody operation should contain code like that
loop select $accept(my_entry_1); $accept(my_entry_2); end select; end loop;
The possible macro is $accept(<operation_name>): it will generate the call of an accept statement
If the operation has an implementation, then it will generate
accept <operation_signature> do <operation_implementation> end <operation_name>;
If the operation has NO implementation, then it will generate
accept <operation_signature>;
The generated code will be
task body Task_1 is begin loop select accept my_entry_1 do -- my_entry_1 implementation end my_entry_1; accept my_entry_2(a : integer); end select; end loop; end Task_1;
Task pragma
Some pragma can be defined on the task and on the task type.
They are defined as constraint with stereotype pragma.
The name of the constraint will give the name of the pragma.
The specification of the constraint will give the parameter of the pragma.
task single_task is pragma priority (12); entry My_Entry_1 (this : in out Class_With_Task_t); end single_task; task type Task_1 is pragma priority (10); entry My_Entry_2 (this : in out Class_With_Task_t); end Task_1; task_1_acc is access task_1;
Task discriminants
Task type discriminants behave like usual class discriminants. They can be added by defining some Discriminant attributes on the AdaTaskType class.
In the example above, the pragma priority specification has been set to "priority", in order to get the value of the discriminant.
task type Task_1 (priority : Integer) is pragma priority (priority); entry My_Entry_2 (this : in out Class_With_Task_t); end Task_1;
Instantiation of task type with discriminants
The discriminants must be setup during instantiation of the task type, like in the example above.
-- dynamic instantiation My_task_instance_1 : my_task_acc_t := new my_task_t(priority => 10); -- static instantiation My_task_instance_2 : my_task_t(priority => 10);
The discriminant value can be defined in parts or global objects. If some values are defined at that level, they will be used to initialize discriminants. Parts or global objects are the only one Rhapsody elements which allow to specify values for discriminants. So it is recommended to instantiate task types by using parts of global objects.
Protected Objects
Protected objects are represented in Rhapsody by a class stereotyped as <<AdaProtectedObject>> or <<AdaProtectedType>> . The result is the creation of an Ada package containing a protected type. The <<AdaProtectedObject>> stereotype should be used for singleton tasks.
Protected objects in Rhapsody.
For <<AdaProtectedObject>> and <<AdaProtectedType>> classes, the Ada_CG.Class.Visibility property has to be set to “Private”
Setting the record type visibility to “Private” for an <<AdaProtectedType>> class
By default, all operations in the class do not represent entries. In order to generate a protected object entry, one has to apply the <<entry>> stereotype to an operation.
Applying the <<entry>> stereotype to a protected object operation
By default, the guard for a protected entry will be set to true, however one can define its own guard by setting the “Ada_CG.Operation.EntryCondition” property to the appropriate boolean expression.
Setting the guard for a protected object/type entry
--++ class Protected_Object_Class package Protected_Object_Class is type Protected_Object_Class_t is tagged limited private; type Protected_Object_Class_acc_t is access all Protected_Object_Class_t; --Public Functions/Procedures section ------------ ----++ operation entry_default_guard() procedure entry_default_guard (this : in out Protected_Object_Class_t); ----++ operation regular_function() function regular_function (this : in Protected_Object_Class_t) return Integer; ----++ operation regular_operation() procedure regular_operation (this : in out Protected_Object_Class_t); private --protected type declaration protected Protected_Object_Class_protected is entry entry_default_guard (this : in out Protected_Object_Class_t); procedure regular_operation (this : in out Protected_Object_Class_t); function regular_function (this : in Protected_Object_Class_t) return Integer; private end Protected_Object_Class_protected; type Protected_Object_Class_t is tagged limited null record; end Protected_Object_Class;
Protected object specification.
--++ class Protected_Object_Class package body Protected_Object_Class is --Functions/Procedures section ------------ procedure entry_default_guard (this : in out Protected_Object_Class_t) is begin Protected_Object_Class_protected.entry_default_guard (this); procedure entry_default_guard; procedure regular_operation (this : in out Protected_Object_Class_t) is begin Protected_Object_Class_protected.regular_operation (this); procedure regular_operation; function regular_function (this : in Protected_Object_Class_t) return Integer is begin Protected_Object_Class_protected.regular_function (this); procedure regular_function; --Protected Object/Type Implementation protected body Protected_Object_Class_protected is entry entry_default_guard (this : in out Protected_Object_Class_t) when true is begin null; --+[ operation entry_default_guard() --+] end entry_default_guard; procedure regular_operation (this : in out Protected_Object_Class_t) is --+[ operation regular_operation().Variables --+] begin null; --+[ operation regular_operation() --+] end regular_operation; function regular_function (this : in Protected_Object_Class_t) return Integer is --+[ operation regular_function().Variables --+] begin return 0; --+[ operation regular_function() --+] end regular_function; end Protected_Object_Class_protected; end Protected_Object_Class;
Protected object body.
--++ class Protected_Type_Class package Protected_Type_Class is type Protected_Type_Class_t is tagged limited private; type Protected_Type_Class_acc_t is access all Protected_Type_Class_t; --Public Variables/Constants static_attribute : Integer; --++ attribute static_attribute --Public Functions/Procedures section ------------ --++ operation entry_function() function entry_function (this : in Protected_Type_Class_t) return Integer; --++ operation entry_true_or_false() procedure entry_true_or_false (this : in out Protected_Type_Class_t); --Public Fields/Variables accessors ------------ function get_attribute_0 (this : in Protected_Type_Class_t) return Integer; pragma inline (get_attribute_0 ); procedure set_attribute_0 (this : in out Protected_Type_Class_t; value : in Integer); pragma inline (set_attribute_0 ); function get_static_attribute (this : in Protected_Type_Class_t) return Integer; pragma inline (get_static_attribute ); procedure set_static_attribute (this : in out Protected_Type_Class_t; value : in Integer); pragma inline (set_static_attribute ); procedure Initialize (this : in out Protected_Type_Class_t); procedure Finalize (this : in out Protected_Type_Class_t); private --protected type declaration protected Protected_Type_Class_protected is entry entry_true_or_false (this : in out Protected_Type_Class_t); function entry_function (this : in Protected_Type_Class_t) return Integer; function get_attribute_0 (this : in Protected_Type_Class_t) return Integer; procedure set_attribute_0 (this : in out Protected_Type_Class_t; value : in Integer); private -- Fields -- attribute_0 : Integer; --++ attribute attribute_0 end Protected_Type_Class_protected; type Protected_Type_Class_protected_acc is access Protected_Type_Class_protected; type Protected_Type_Class_t is tagged limited record my_Protected_Type_Class_protected : Protected_Type_Class_protected_acc; end record; end Protected_Type_Class;
Protected type specification.
With UNCHECKED_DEALLOCATION; --++ class Protected_Type_Class package body Protected_Type_Class is --Functions/Procedures section ------------ procedure entry_true_or_false (this : in Protected_Type_Class_t) is begin this.my_Protected_Type_Class_protected.entry_true_or_false (this); end entry_true_or_false; --Fields/Variables accessors ------------ function get_attribute_0 (this : in Protected_Type_Class_t) return Integer is begin return this.my_Protected_Type_Class_protected.get_attribute_0 (this); end get_attribute_0; procedure set_attribute_0 (this : in out Protected_Type_Class_t; value : in Integer) is begin this.my_Protected_Type_Class_protected.set_attribute_0 (this, value); end set_attribute_0; --Protected Object/Type Implementation protected body Protected_Type_Class_protected is entry entry_true_or_false (this : in out Protected_Type_Class_t) when true or false is begin null; --+[ operation entry_true_or_false() --+] end entry_true_or_false; function get_attribute_0 (this : in Protected_Type_Class_t) return Integer is begin return attribute_0; end get_attribute_0; procedure set_attribute_0 (this : in out Protected_Type_Class_t; value : in Integer) is begin attribute_0 := value; end set_attribute_0; end Protected_Type_Class_protected; procedure Initialize (this : in out Protected_Type_Class_t) is begin this.my_Protected_Type_Class_protected := new Protected_Type_Class_protected; end Initialize; procedure Finalize (this : in out Protected_Type_Class_t) is procedure FREE is new UNCHECKED_DEALLOCATION( Protected_Type_Class_protected, Protected_Type_Class_protected_acc ); begin FREE(this.my_Protected_Type_Class_protected); end Finalize; end Protected_Type_Class;
Protected type body.
The protected type generates the constructor and destructor as well, which create the task instance and destroy it.
Entrypoints
An entrypoint can be created in Rhapsody to represent the starting point of the Ada program. This is done by stereotyping a class as <<Entrypoint>> .
An entrypoint in Rhapsody.
In addition, define an operation on the class, and enter the implementation of the operation to complete the entrypoint. The result is a single package body file generated in myEntrypoint.adb.
Procedure myEntrypoint is --+[ operation main().Variables --+] begin --+[ operation main() --entrypoint implementation --+] end myEntrypoint;
The entrypoint definition.
In order to use only this user defined entrypoint (and not default entrypoint) in the model, the CG::Configuration::MainGenerationScheme property must set to "UserInitializationOnly".
Singleton Classes
Singleton classes represent classes that have only one instance. This is represented in Rhapsody by stereotyping the class <<Singleton>> . A singleton class creates a private variable to contain the singleton instance, and all non-static operations access this instance instead of passing in a ‘this’ parameter.
A singleton class in Rhapsody.
Ada 95
When the singleton class is generated using the Ada 95 rules, the non-static attributes are held in a record in the same manner as a normal class.
--++ class My_Singleton package My_Singleton is type My_Singleton_t; type My_Singleton_acc_t is access all My_Singleton_t; type My_Singleton_t is tagged limited record; -- Fields -- my_Int : Integer; --++ attribute my_Int end record; --Public Variables/Constants my_Static_Int : Integer; --++ attribute my_Static_Int --Public Functions/Procedures section ------------ --++ operation my_Operation() procedure my_Operation; --++ operation my_Static_Operation() procedure my_Static_Operation; --Public Fields/Variables accessors ------------ function get_my_Int return Integer; pragma inline (get_my_Int); procedure set_my_Int (value : in Integer); pragma inline (set_my_Int); function get_my_Static_Int return Integer; pragma inline (get_my_Static_Int); procedure set_my_Static_Int (value : in Integer); pragma inline (set_my_Static_Int); private end My_Singleton;
The package specification for a singleton class in Ada 95.
--++ class My_Singleton package body My_Singleton is -- Singleton storage My_Singleton_unique_instance : aliased My_Singleton_t; -- short names for singleton attributes attribute_0 : Integer renames My_Singleton_unique_instance.attribute_0; --Functions/Procedures section ------------ procedure my_Operation is --+[ operation my_Operation().Variables --+] begin null; --+[ operation my_Operation() --+] end my_Operation; procedure my_Static_Operation is --+[ operation my_Static_Operation().Variables --+] begin null; --+[ operation my_Static_Operation() --+] end my_Static_Operation; --Fields/Variables accessors ---------------- function get_my_Int return Integer is begin return My_Singleton_unique_instance.my_Int; end get_my_Int; procedure set_my_Int (value : in Integer) is begin My_Singleton_unique_instance.my_Int := value; end set_my_Int; function get_my_Static_Int return Integer is begin return My_Singleton_unique_instance.my_Static_Int; end get_my_Static_Int; procedure set_my_Static_Int (value : in Integer) is begin My_Singleton_unique_instance.my_Static_Int := value; end set_my_Static_Int; private end My_Singleton;
The package body for a singleton class in Ada 95.
Ada 83
Changing the “DefaultComponent” component to be an Ada 83 package changes the generation of the singleton class so that the rules for Ada 83 are followed. In this case, all attributes are considered static attributes and a record type is not created for the class nor is a variable for the singleton instance created in the package body.
Changing the component to generate Ada 83 code.
--++ class My_Singleton package My_Singleton is my_Int : Integer; --++ attribute my_Int --Public Variables/Constants my_Static_Int : Integer; --++ attribute my_Static_Int --Public Functions/Procedures section ------------ --++ operation my_Operation() procedure my_Operation; --++ operation my_Static_Operation() procedure my_Static_Operation; --Public Fields/Variables accessors ------------ function get_my_Int return Integer; pragma inline (get_my_Int); procedure set_my_Int (value : in Integer); pragma inline (set_my_Int); function get_my_Static_Int return Integer; pragma inline (get_my_Static_Int); procedure set_my_Static_Int (value : in Integer); pragma inline (set_my_Static_Int); private end My_Singleton;
The package specification for a singleton class in Ada 83.
--++ class My_Singleton package body My_Singleton is --Functions/Procedures section ------------ procedure my_Operation is --+[ operation my_Operation().Variables --+] begin null; --+[ operation my_Operation() --+] end my_Operation; procedure my_Static_Operation is --+[ operation my_Static_Operation().Variables --+] begin null; --+[ operation my_Static_Operation() --+] end my_Static_Operation; --Fields/Variables accessors ---------------- function get_my_Int return Integer is begin return my_Int; end get_my_Int; procedure set_my_Int (value : in Integer) is begin my_Int := value; end set_my_Int; function get_my_Static_Int return Integer is begin return my_Static_Int; end get_my_Static_Int; procedure set_my_Static_Int (value : in Integer) is begin my_Static_Int := value; end set_my_Static_Int; private end My_Singleton;
The package body for a singleton class in Ada 83.
Relations
Relations may need the use of containers, depending of their kind. Usually, the used containers are Booch Components. This container is not provided any more with Rhapsody Developer for Ada. The code generator provides the possiblity to choose any kind of containers. This is selected with the property "Ada_CG.Configuration.ContainerSet". This property can take the following values:
- default : this value allows the code generator to generate code with Booch Components and "old implementation".
- Ada2005Container: this value allows the code generator to generate code which uses Ada2005 containers with "custom container". In this case, the model is a little bit different, and code to be generated is specified inside some properties.
In the documentation we will identify the default container by "old implementation", and Ada2005container by "custom container". "custom container" can be modified or created by user, by using properties. This allows a very flexible implementation of containers.
The two next paragraphs describes old implementation, and the next one descibes the representation, and customization of custom containers.
Unidirectional Relations
This paragraph describes old implementation of unidirection relations. If you need to use a custom container, refer also to "custom container".
There are three different options for the implementation of relations. Each implementation creates an Ada record field and accessor(s) and mutator(s) methods and possibly some new types and inner packages to support the implementation. The name of the Ada record field is the name of the role for the relation.
The implementation kind is controled by the property "CG.Relation.Implementation".
Default implementation : The default implementation uses an access type for the target object. The access type is held in an Ada record field. In addition, a “With” statement is added for the target package so that the access type is visible.
Fixed implementation : The fixed implementation uses a direct reference to the target object. The target object type is held in an Ada record field. As with the “Default” implementation, a “With” statement is added for the target package so that the target type is visible.
Scalar implementation : The scalar implementation is better described as the index implementation. Instead of holding a direct reference to either the target type or an access to the target type, a numerical index is used instead. The intent is that there will be a container object in the system that will hold the instances of the target class, and that the index is used to retrieve the correct instance from the collection. In this case, a “With” statement is not created for the target package because only an index is stored in the class, and not a reference to the target object itself. Instead, a new type is created to represent the valid range for the indexBi-directional relations are not supported at this time, nor are unbounded multiplicities.
Multiplicity = 1
When the multiplicity = 1, the class will have a reference to only one instance of the target class. The following example demonstrates the generated code for each of the implementation types. Note that no accessors are generated for the “Fixed” implementation.
Class relations with multiplicity = 1.
Multiplicity > 1, general notes
When the multiplicity is greater than 1, the same basic concepts are followed for each implementation choice, except some data structures are created to hold the instances. In addition, new types are defined to represent the valid indices into such structures, and iterator subpackages are declared for every relation..
Unbounded relations and qualified relations (both bounded and unbounded) use data structures that rely on the Booch components.
Generated Method |
Relation type | |||
Bounded | Unbounded | Bounded Qualified | Unbounded Qualified | |
Get_At_Pos (Procedure) | Y | Y | ||
Get_At_Pos (Function) | Y | Y | ||
Contains | Y | Y | ||
Get_Count | Y | Y | Y | Y |
Set_At_Pos | Y | Y | ||
Add_At_Pos | N | Y | ||
Remove | N | Y | ||
Remove_At_Pos | N | Y | ||
Get(Key) (Procedure) | Y | Y | ||
Get(Key) (Function) | ||||
Contains(Key) | Y | Y | ||
Set(Key) | Y | Y | ||
Remove(Key) | Y | Y |
Table 2 Nary relations methods matrix
Method Name | Description |
Initialize | Creates the iterator |
Get_Next | Gets next element |
To_Value | Get value associated to current iterator position |
Is_Last | Return true if there’s no more elements to iterate over. |
Table 3 Nary relations Iterator package method description
Details on the Booch components
Rhapsody Developer for Ada does not install Booch Components files. If needed, user must do it manually by following the procedure. See section Installation notes: Booch components .
Either the original Ada 83 version of the components can be used, or the Ada 95 version. The choice is made at the component level with the Ada_CG.Component.UseBoochComponents property.
Although only part of it is used. Here is the list of packages that may be with’ed by the generated code when using Ada 83:
- semaphore
- storage_manager_concurrent
- list_single_unbounded_controlled
- list_utilities_single
- list_search
- map_simple_noncached_concurrent_bounded_managed_noniterator
- map_simple_noncached_concurrent_unbounded_managed_noniterator
Bounded | Unbounded | |
Unqualified relations | None | 1,2,3,4,5 |
Qualified relations | 1,6 | 1,2,7 |
Table 4 Booch 83 Components Package Dependency Matrix
Here is the list of packages that may be with’ed by the generated code when using Ada 95:
- BC.Support.Standard_Storage
- BC.Containers.Collections.Unbounded
- BC.Containers.Maps.Unbounded
- BC.Containers.Maps.Bounded
Bounded | Unbounded | |
Unqualified relations | None | 1,2 |
Qualified relations | 4 | 1,3 |
Table 5 Booch 95 Components Package Dependency Matrix
Multiplicity > 1 , bounded
An array is created to hold the instances
Class relations with multiplicity > 1, bounded.
Multiplicity > 1, unbounded
Unbounded relations are no longer represented as arrays of 100 elements, but as data structures relying on dynamic memory allocation.
Important note : Any legacy code using unbounded relations relying on the fact that the underlying implementation is an array of 100 elements MUST be updated, as it will either not compile or potentially lead to run-time errors.
Multiplicity > 1, qualified relations
Qualified relations are represented with maps. The unbounded form relies on dynamic memory allocation.
A qualified relation is a key based association. This means that if there is an association from A to B, where the qualifier is an attribute B.id of type Integer, the relation is key based (in this case, Integer is the key type).
Only one element can be bound for each value of the qualifier domain.
Adding an element with a key that already exists has the effect of replacing the old element with the new one.
Note : only attributes of a type that is a subtype of Integer can be used as Qualifiers.
Note : with booch 95 components, if the type of the key is a subtype of integer (or any standard type) which is defined in an other package, then user must define a new “=” function in this other package.
For example, a new subtype of integer is defined into User_Type package.
User must define a new “=” function into this package.
package User_Type is subtype My_Subtype_Integer is Integer range 1..1000; function "=" (A : in My_Subtype_Integer; B : in My_Subtype_Integer) return Boolean; private end User_Type; package body User_Type is function "=" ( A : in My_Subtype_Integer; B : in My_Subtype_Integer ) return Boolean is begin return standard."="(A, B); end "="; end User_Type;
Bidirectional Relations
Rhapsody Developer for Ada provides two implementations for bidirectional relations. By setting the Ada_CG.Relation.BidirectionalRelationsScheme to SubtypingAndRenaming (selected by default) or IntermediateParentClasses, it is possible to select which one is to be used.
SubtypingAndRenaming scheme
Implementation principles
This implementation supports bidirectional relations via the following mechanisms:
- the actual class members for classes participating in bidirectional relations are all generated in the same package so as to get reciprocal visibility.
- The classes are “emulated” in packages made up of subtyping and renaming of the class members.
Limitations
Note that there are limitations applicable to classes participating in bidirectional relations using that scheme which are described hereafter:
- They shall not contain elements with the same name (for types, static attributes or association ends role names) or signatures (for operations).
- They shall not be template classes
- Deferred initialization of public constants is not supported.
- They shall not contain statechart code
- They shall not have triggered operations
- Roundtrip is not supported
- Ports are not supported
IntermediateParentClasses scheme
Implementation principles
This scheme supports bidirectional relations via the following mechanisms:
- For each class participating in a bidirectional relation, an intermediate parent class is generated and inserted into the inheritance hierarchy.
- The bidirectional relations pointing to this class are redirected to point to its intermediate parent.
Using
Example:
Classes class_1 and class_2 have bidirectional relation.
If class_2 wants to get instance of "itsclass_1", then the following accessor should be used:
get_downcast_itsClass_1 (this : in class_2_t)
Limitations
Most of the limitations of the SubtypingAndRenaming scheme are no longer relevant with this implementation. However there are still a few remaining limitations:
- This implementation is not compatible with Ada83 only configurations.
Custom Containers
Choice of container set
Container set is selected by using the property "Ada_CG.Configuration.ContainerSet". Two kind of container set are provided
- default: this option uses legacy code generator which implements Booch components
- Ada2005Container: this option provides Ada2005 containers, using new containers' implementation.
If user needs it, he can define himself its own container, by creating new properties. This will be explained below.
Choice of container's implementation
Container's implementation is chosen by using the property "CG.Relation.Implementation". The values are:
- default
- StaticArray
- Bounded
- UnBounded
- BoundeQualified
- UnboundeQualified
If default value is used, the code generator will choose himself the implementation, depending of he kind of relation and its multipicity.
multiplicity | qualified relation | implementation |
1..n | no | staticArray |
1..n | yes | BoundedQualified |
1..* | no | Unbounded |
1..* | yes | UnboundedQualified |
User has the possiblity to define other kind of implementation, if the provided ones are not suitable. The way to do it is explained below.
Choice of element's kind inside the container
The type of the element stored inside the container, can be controled with the property "Ada_CG.Relation.AccessTypeUsage". This property can take the following values:
- None : the element's type stored inside the container will be a class record type
- Regular : the element's type stored inside the container will be an regular access to the class record type
- ClassWide : the element's type stored inside the container will be an class-wide access to the class record type
Build a new container set
User has the possibility to modify or create generated code for containers. This paragraph will explain in details all the steps to create a new container set
Create siteAda.prp file
The container's description is done inside sodius.prp file. If some new properties need to be modified or created, a new file "siteAda.prp" must be created. This new file will be automatically recognized, when the model is loaded.
Create a new container
In this example, we will create a new container set. Its name will be "NewContainers". The property "Ada_CG.Configuration.ContainerSet" must be updated in order to be able to select this new container, and a new subject is create to describe the container. The following text should be added in the file:
Subject Ada_CG Metaclass Configuration Property ContainerSet Enum "Default, Ada2005Containers, NewContainers" "Default" end end Subject NewContainers ... end
The "NewContainers" subject should contain some metaclass who are described below
Create a General metaclass
The General metclass contains some properties which are used to generate make files. The name of these properties respect a naming rule, in order to fit many situation. The name is constructed like that:
<environement_name>CompilerInclude[<implementation_name>]
The implementation name is optional. In this case, the content of this property will be added in makefile for any kind of implementation. If the implementation name is added, then this code is added depending on implementation kind.
Here is an example of values of these properties for GNAT compiler and ObjectAda compiler, for Booch components.
Subject NewContainers metaclass General Property GnatCompilerInclude String "-aI%OMROOT%\\LangAda95\\booch_ada_95\\src\\" Property ObjectAdaCompilerIncludeBounded String "" Property ObjectAdaCompilerIncludeUnbounded String "adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-standard_storage.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-unbounded.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-unbounded.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-collections.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-collections.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-collections-unbounded.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-collections-unbounded.adb " Property ObjectAdaCompilerIncludeBoundedQualified String "adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-maps.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-maps.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-maps-bounded.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-maps-bounded.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-hash_tables.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-hash_tables.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-bounded_hash_tables.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-bounded_hash_tables.adb " Property ObjectAdaCompilerIncludeUnboundedQualified String "adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-standard_storage.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-unbounded.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-support-unbounded.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-maps.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-maps.adb adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-maps-unbounded.ads adareg %OMROOT%\\LangAda95\\booch_ada_95\\src\\bc-containers-maps-unbounded.adb " end end
Create implementation
The minimum list of implementations is
- StaticArray
- Bounded
- UnBounded
- BoundeQualified
- UnboundeQualified
The siteAda.prp file must contains this code
Subject NewContainers metaclass General Property GnatCompilerInclude String "" Property ObjectAdaCompilerIncludeBounded String "" Property ObjectAdaCompilerIncludeUnbounded String "" Property ObjectAdaCompilerIncludeBoundedQualified String "" Property ObjectAdaCompilerIncludeUnboundedQualified String "" end metaclass StaticArray ... end metaclass Bounded ... end metaclass Unbounded ... end metaclass BoundedQualified ... end metaclass UnboundedQualified ... end end
For each metaclass, a list of properties must be defined in order to specify which code needs to be generated.
If needed, user can define other kind of implementation. For that, a new metaclass needs to be created with the name of the new implementation. The property "CG.Relation.Implementation" will have to be updated with this new implementation. The property must be copied from sodius.prp to siteAda.prp, and updated with the new implementation name
Subject CG Metaclass Relation Property Implementation Enum "Default,...,NewImplementation" "Default" end end Subject NewContainers metaclass General Property GnatCompilerInclude String "" Property ObjectAdaCompilerIncludeBounded String "" Property ObjectAdaCompilerIncludeUnbounded String "" Property ObjectAdaCompilerIncludeBoundedQualified String "" Property ObjectAdaCompilerIncludeUnboundedQualified String "" end metaclass StaticArray ... end metaclass Bounded ... end metaclass Unbounded ... end metaclass BoundedQualified ... end metaclass UnboundedQualified ... end metaclass NewImplementation ... end end
Create implementation's code
For each metaclass, a set of properties will define the code to be generated. The text of the properties will be copied inside the generated code. The text can contain some macro which will be executed, and replaced by the result of their execution.
Their are properties for different parts of the code.
Subject NewContainers ... metaclass Bounded Property WithClauseInSpec MultiLine "" Property WithClauseInBody MultiLine "" Property TypeDeclaration MultiLine "" Property TypeDeclarationAfterRecordType MultiLine "" Property TypeDeclarationInBody MultiLine "" Property RelationType String "" Property IndexType String "" Property GetAtPosProcedure MultiLine "" Property GetAtPosFunction MultiLine "" Property Contains MultiLine "" Property GetCount MultiLine "" Property Set MultiLine "" Property Add MultiLine "" Property AddAtPos MultiLine "" Property Remove MultiLine "" Property RemoveAtPos MultiLine "" end ... end
- WithClauseInSpec : this property contains the with clause declaration in specification file.
- WithClauseInBody : this property contains the with clause declaration in body file.
- TypeDeclaration : this property contains any declaration (type or package or operation) which will be generated before the class record type. It is typicaly used to declare the type of the relation.
- TypeDeclarationAfterRecordType : this property contains any declaration (type or package or operation) which will be generated after the class record type.
- TypeDeclarationInBody : this property contains any declaration (type or package or operation) which will be generated in body file. It will be generated on top of the body file.
- RelationType : this property contains the name of the relation's type. It is used to generate the name of the relation's type in class record type.
- IndexType : this property contains the name of the index's type. If this property is empty, or do not exist, then the index's type is "Positive". It is used to define the type of the index in signature of operations which have an index as argument.
- GetAtPosProcedure : this property contains the implementation of the procedure get_at_pos().
- GetAtPosFunction : this property contains the implementation of the function get_at_pos().
- Contains : this property contains the implementation of the function contains().
- GetCount : this property contains the implementation of the function getCount().
- Set : this property contains the implementation of the procedure set_at_pos().
- Add : this property contains the implementation of the procedure add().
- AddAtPos : this property contains the implementation of the procedure add_at_pos().
- Remove : this property contains the implementation of the procedure remove().
- RemoveAtPos : this property contains the implementation of the procedure remove_at_pos().
It is possible that implementation of an operation changes, depending on "accessTypeUsage" defined in property "Ada_CG.Relation.AccessTypeUsage". In this case it is possible to create another property which specifies the accessTypeUsage. For that the value of the accessTypeUsage must be added to the name of the property. The possible use cases are:
- GetAtPosProcedure
- GetAtPosProcedureRegular
- GetAtPosProcedureClassWide
- GetAtPosFunction
- GetAtPosFunctionRegular
- GetAtPosFunctionClassWide
If the property is not present or is empty, the operation will not be generated.
The signature of the operations are autogenerated. User can control
- the name of the operation with the property "Ada_CG.Relation.<operation>"
- the index's type of the operation which needs an index, with the property "<Container>.<implementation>.IndexType"
In order to generate code for any relation in the model, some macro must be inserted in the text of the properties. Any valid rule can be used. The name of the macro must start with "$" or must be enclosed by double bracket "[[" "]]".
Property TypeDeclaration MultiLine "type [[Name]]_card_t is new Positive range 1..$Multiplicity_Upperbound; package [[Name]]_Unbounded_list is new Ada.Containers.Vectors([[Name]]_card_t, $ADA_Relation_Target_Element_Type_Name, \"=\" => $ADA_Relation_Target_Class_Full_Name.\"=\");"
macro | description |
Name |
Gives the name of the relation itsClass_2 |
ADA_Relation_Target_Element_Type_Name |
gives the type of the target. The result depends on the value of the property "Ada_CG.Relation.AccessTypeUsage" Package_0.class_2.class_2_acc_t |
ADA_Relation_Target_Class_Full_Name |
gives the full name of the target Package_0.class_2 |
ADA_Relation_Qualifier_Full_Regular_Type_Name | Gives the full regular name of the type of the qualifier of the relation |
ADA_This_As_Formal_Parameter_In_List_Mode_In |
Gives the this parameter to be generated, when an operation is declared in "TypeDeclaration" this : in class_1_t |
ADA_Relation_Target_Name |
Gives the name of the relation itsClass_2 |
ADA_Relation_Target_Collection_Name |
Gives the target collection name to be set to call an operation of this target. this.itsClass_2 |
Ports
Limitations
Note that there are limitations applicable to usage of ports which are described hereafter:
- Ports contracts have to be implicit
- Multiplicities in links between ports have to be balanced
-
- The number of source instances has to match the number of target instances
- And the number of source ports has to match the number of target ports
- A port can have multiple contracts if the model is built for Ada 2005
- Fast ports are available only is the model is built for Ada 2005
Using ports
RiA generates code for ports in classes and for linking instances via ports.
When a class has ports, the code generator will create an additional Ada package called <class>_port. This Ada package contains all the material to declare the class’s ports. The class contains a part of this <class>_port type.
Some functions are added in order to send some messages through ports.
Get_<Port_Name>(this : class_type) : return port_type
This function gets the instance of the port we need.
<message>(this: class_type, port : port_type)
This function sends the message “message” through the port “port” of the class “this”. One function is created for each message defined in the port’s interface. The usual way to send a message is to write:
<message>(this, Get_<Port_Name>(this));
If some parameters need to be passed with the message, then they are added after the port’s instance:
<message>(this, Get_<Port_Name>(this), param : param_type);
The procedure is the same when you want to send an event through a port. Use the gen event function with port’s name:
Gen_<event_name>(this, Get_<Port_Name>(this));
When an event is received from a port, it is possible to know which port received it. It is done with the function Is_Port of class Oxf.I_Event. Its signature is:
oxf.I_Event.is_port(event : oxf.I_event_t, port_ID : System.address)
The port_ID is given by the address of the port. You must get it like that:
<port_name>.Get_Inbound(Get_<port_name>('this)).Port_ID
Example 1 : behavioral port
Create a new model with 2 classes Class_0 and Class_1 which are parts of a class Build.
Create an interface interface_1 with one operation “message_0”.
Create port “Port_0” on Class_0.
Open features window of this port. Check behavioral check button, and add a provided interface in contract tab.
Copy this port in Class_1.
Open features window of this new port. Check reversed check button
Create a link between ports of the two classes.
Add an operation message_0() in class_0. Its implementation must be:
put_line("class_0 : message_0()");
Add an operation test() in class_1. Its implementation must be:
put_line("Class_1 : test()"); message_0(this,get_port_0(this));
Add an put_line() in class Build. Its implementation must be:
put_line("Build : test()"); class_1.test(this.itsClass_1.all)
Add a with and use clause for Ada.text_IO in all classes.
In configuration features, initialize the Build class in Initialization tab, and implement initialization code with:
Build.test(p_Build.all);
You should get a model like this one:
In this model test() function of class_1 will send message “message_0” through its port port_0.
Example 2 : fast ports
Take the model created below.
Set Ada_CG:Component:AdaVersion Property to Ada05 in oder to build the model for Ada 2005.
In Class_0, Add a statechart with 2 states and one event “a” between the 2 states
In Class_1, Add a statechart with 2 states and one event “b” between the 2 states
Remove the contract of the 2 ports
Change the implementation of class_1.test():
gen_event(this, Default.get_a, get_port_0(this));
To send an event through a fast port you must use the function Gen_Event. Its signature is:
gen_event(this: class type, event : Oxf.Event.Event_acc_t, port : port_type);
The event must be created with the function defined in the event’s package. Its name is:
<event package>.get_<event_name>
Multicast ports
This feature allows sending a message through one port to several ports in a single operation. It uses Booch components 95 in order to create an unbounded list of interfaces. Booch components are not provided with Rhapsody install. They must be installed manually if needed. (See Installation notes: Booch components for more information.)
Multicast generation is controlled by the property
ADA_CG::Port::Support Multicasting
This property has the following values
Never : multicast instrumentation is never used
Smart : multicast instrumentation is used only if needed (see algorithm below)
Always : multicast instrumentation is always generated
Sending a multicast message
Sending a message to a port will automatically send it to all connected interfaces. The syntax is the same than with a single interface.
Example
The port Port_0 of an instance of class Class_0 is linked to several ports of instances of various other classes( which have the same interface as port_0)
To send a message “message” through Port_0 of class_0 you must write:
message(this, get_port_0(this));
A message can be a procedure, an event or a triggered operation. Functions cannot be sent through multicast ports.
Fast ports also support multicast.
Link initialization with multiplicity
Multicast can be automatically initialized only if required and provided interface of the link belongs to class instance of multiplicity 1 and if its port has also multiplicity 1. In this case there is no ambiguity for initializing the links. Multiplicity of both ends of the link must be equal to 1.
Multiplicity equals 1 in both ends of the link (in this case multicast is not useful)
There are several instances of a class with provided interface.
Other operations on multicast ports
Some other operations can be done on a multicast Port in order to control the links between ports.
- Add a new provided interface
- Remove an existing provided interface
- Send a message to only one provided interface.
A provided interface can be disconnected from required interface. The following procedure does this.
Remove_<interface_name>(this : port_type, interface : interface_type);
Example:
declare currentSourcePort : microphone_port.port_0.port_type; currentTargetPort : loudspeaker_port.port_0.port_type; begin currentSourcePort := microphone.get_port_0(this.itsMicrophone_0.all); currentTargetPort := loudspeaker.get_port_0(this.itsLoudspeaker_0.all); microphone_port.port_0.remove_Interface_9 ( currentSourcePort, loudspeaker_port.port_0.get_Interface_9(currentTargetPort) ); end;
To add a new link to port, you just need to set port interface as usual.
Example:
declare currentSourcePort : microphone_port.port_0.port_type; currentTargetPort : loudspeaker_port.port_0.port_type; begin currentSourcePort := microphone.get_port_0(this.itsMicrophone_0.all); currentTargetPort := loudspeaker.get_port_0(this.itsLoudspeaker_0.all); microphone_port.port_0.set_Interface_9 ( currentSourcePort, loudspeaker_port.port_0.get_Interface_9(currentTargetPort) ); end;
It is possible to send a message to only one link. A message “Message” can be sent to only one provided interface:
Message(this : class_type, port : port_type, interface : interface_type);
Variation Points
Variation points and there variants can be created like in CPP.
If no variant is selected at component level, for a specific variation point, then the variation point will be generated as if it is a usual class.
If a variant is selected then a package which defines a renamed package is generated for this variation point.
with Screen_HDMI; package display renames Screen_HDMI;
The files generated for the variant will be generated from the variant class. The Ada package name will be the name of the variant class. The name of the class types will be the name of the variation point class.
--++ class Screen_HDMI package Screen_HDMI is type display_t; type display_acc_t is access all display_t; type display_t is tagged null record; --Public Functions/Procedures section -------- --++ operation println(String) procedure println (this : in out display_t; text : in String ); private end Screen_HDMI;
Active Variation Points in SXF mode
Static Generalization
Static generalization is represented by a generalization which has the stereotype “Static“.
In this case all the elements of the parent class (type, attributes, operation, etc...) will be generated in the child class.
In the example above, the Ada package of the class Child_1 will declare the following operations:
- operation test(): this operation is defined in parent class
- operation test_2(): this operation is defined in child class, and ovewrites the parent's one
- operation test_3(): this operation is defined in child class
Pragma
There are many possibilities to generate pragmas, depending on which kind of pragma we want to generate, and where we want to generate it. This chapter makes the list of all possibilities, and proposes a generic method to generate any kind of pragma.
Elaboration Pragmas
Pragmas on class and package
Some properties allow setting some user defined pragmas in particular places in spec files or body files of classes or packages. These properties are described here.
Some additional tags on class, enable generating pragma atomic and pragma volatile for the class record type.
Pragmas on Ada task
For classes which have the stereotype D_AdaTaskType, some properties allow setting pragma Priority or pragma Storage_Size. This is described here.
Pragmas on user defined Ada task
For user defined task (class with stereotype "AdaTaskType), a constraint with "pragma" stereotype can be defined. This is described here.
pragma on static attribute
Some tags defined on attribute, can be checked to define:
- pragma atomic
- pragma volatile
Some accessor and mutator can be automaically generated for attributes. Some properties defined on attribute, allow generating pragma inline on these accessor and mutator.
- Ada_CG::Attribute::InlineAccessor
- Ada_CG::Attribute::InlineMutator
pragma on type
Some tags defined on type, can be checked to define:
- pragma atomic
- pragma volatile
pragma on operation
A pragma inline can be generated if the check box "inline" is checked in general tab of operation feature.
generic pragma
If you need to generate other not predefined pragma, another generic representation can be used. A pragma is represented by a constraint with "pragma" stereotype. This pragma can be added on several Rhapsody elements.
- Package
- Class
- Operation
- Attribute
- Type
- AdaTaskType
To add a pragma to an element, in the browser, right click on the element, and in the menu, select Add New>AdaCodeGenegration>pragma.
Creating a pragma on a variable.
The pragma identifier is set by the Rhapsody pragma name.
The pragma parameter is set in the Rhasopdy pragma specification. It is possible to add some macro which will be replaced by some other text.
- %name: this macro will be replaced by the name of element which contains the pragma
- %recordTypeName: this macro is used only for pragma which is set on a class. It will be replaced by the name of the class record type.
Setting the pragma features.
The generated code for this pragma defined above will be:
variable_0 : integer; pragma atomic(variable_0);
The pragma is generated after attribute, type and operation declaration. Pragmas defined on class and package can be generated at several places. The place where the pragma is generated is controled by two tags.
- Position: this tag can take several values
- before_with_clause: the pragma is generated before with clause in context clause section of the file.
- after_with_clause: the pragma is generated after with clause in context clause section of the file.
- in_declarative_part: the pragma is generated after the declaration of the Ada package.
- for_class_racord_type: the pragma is generated after the class record type declaration.
- Visibility: This tag can take Two values
- Specification: the pragma is generated in specification file.
- Body: the pragma is generated in body file.
In some cases, we should need to generated the pragma for some configuration, and not for an other configuration. The choice of the configurations for which the pragmas must be generated is done with a dependency from the pragma to the configuration. The policy is the following:
- If the pragma has no dependency, then it is always generated. This is the default case.
- If the pragma has some dependency to some configuration, then it will be generated only for these configurations.
Ada Libraries
Creating an Ada Library
An Ada library can be created from a project by setting the “Library” option on the component.
Setting the Component to Create a Library.
When the project is built, a library will be created in the directory specified by the configuration using the naming conventions described in the table below.
Compiler(s) | Library naming convention |
GNAT | Lib<ComponentName>.a |
ObjectAda / Win32 | <ComponentName>.lib |
ObjectAda / Raven | N/A |
Table 6 Compilers library naming conventions
Linking an Ada Library
To use an Ada library from another project, two pieces of information are required in the component properties. The first is the name of the library to use. This name depends on the compiler you are using, the syntax is described in the previous table. This name is put in the “Libraries” field. If there is more than one library to list, place a carriage return between the names.
Using an Ada Library.
The location of the libraries also needs to be specified. The “Include Path” field is used to capture this information. The location of the library as well as the location of the sources for the library must be included. If there is more than one path to enter a carriage return should be used as a separator.
Configuration of Main File Generation
If the property “CG.Configuration.MainGenerationScheme” is set to “Full” on the configuration being generated, an entrypoint will automatically be created. The entrypoint will be named main<Component Name>.adb, and will produce an executable called <Component Name>.exe. This entrypoint will overwrite the output from any user-created entrypoint in the model.
Configuration Instances.
With Clauses
A “With” Clause will be created for every class selected.
Configuration Prolog
The contents of the “Ada_CG.Configuration.ImplementationProlog” property on the configuration will appear just after the “With” clauses. It can be used to “With” other classes or packages as needed.
Instance Creation
If the selected class is not a singleton, a variable will be created to hold an access to the type of the class, and initialized with a new instance. If the class implements an Initialize procedure, the new instance will be initialized as well. If the class is a singleton and implements the Initialize procedure, the procedure will be called on the class.
RiADefaultActive Initialization
If there is a reactive class in the model that requires the RiADefaultActive class, the RiADefaultActive class will be initialized.
Reactive Instance Hookup
If there is both an instance of a reactive class, and an instance of the active context for this reactive class, the reactive instance will be registered on the active instance. If the reactive instance uses the RiADefaultActive class, this registration will be done as well.
Start Behavior
The RiADefaultActive class will be started if needed
As for the configuration initial instances, the Ada_CG.Relation.ObjectInitialization (Creation, Full, None) configuration property controls their initial behavior. By default it is set to “Full”, which means instances will be initialized and their behavior will be started. If the user would like the behavior not to be started, “Creation” should be selected.
User defined local variables
Variables declared in the “Ada_CG.Configuration.LocalVariablesDeclaration” property will appear in the declaration of the entrypoint.
User Initialization Code
Any code entered in the “Initialization Code” field on the configuration will be inserted into the entrypoint.
Configuration Epilog
The contents of the “Ada_CG.Configuration.ImplementationEpilog” property on the configuration will appear just after the “end MainDefaultComponent;” line.
with RiA_Default_Active; with class_0; with class_1; procedure MainDefaultComponent is start_behavior_status : Boolean; p_class_0 : class_0.class_0_acc_t; p_class_1 : class_1.class_1_acc_t; begin -- Instance Initialization RiA_Default_Active.Initialize; p_class_0 := new class_0.class_0_t; class_0.Initialize(p_class_0.all ); p_class_1 := new class_1.class_1_t; -- register Reactive Classes RiA_Default_Active.register_context_class_0(p_class_0.all); -- Start Behavior RiA_Default_Active.start; class_0.start_behavior(p_class_0.all , start_behavior_status); -- Initialize Package Instances -- User Initialization Code -- Your Configuration Initialization Code end MainDefaultComponent;
Auto-generated Entrypoint.
The generated entrypoint can be viewed by selecting “Edit Configuration Main File” from the Configuration.
If animation is turned on, these instances will NOT be animated.
Instances Defined on a Package
Package Modifications
A child package will be created to handle the creation and initialization of any instances defined in a package. The name of the package will be <<Ada Package Name>>.RiA_Instances, where <<Ada Package Name >> is the name of the package where the instances are defined. When generating Ada83, the package name will be <<Ada Package Name>>_Instances. Each instance defined in the UML package will create global variables in this generated Ada package.
Global Instances on a Package.
Global Instance with Mulitplicity = 1.
Global Instance with Multiplicity > 1.
Variables will be created in the public part of the package specification for each instance defined. The type of variable will depend on the setting of the “CG.Relation.Implementation” property for each of the relations. If the instance is a singleton, no variable will be created. If the multiplicity is greater than 1 but not *, an array with be used of the given size. If the multiplicity is given as *, an array will be generated of size 100. The elements in this array will not be initialized.
The appropriate “With” statements will be added to the package specification for each Class instantiated.
These instances will be created in the procedure Initialize_Relations , and they will be initialized if an Initialize operation exists for their class.
Likewise, these instances will be finalized in the procedure Finalize_Relations if a Finalize operation exists for their class.
If the instances have a statechart, the start_behavior procedure will be called to start the behaviors of the instances in the Initialize_Relations procedure. The instances will be hooked up to their active context if needed as well. If the instances are active, they will be started by calling the start procedure.
with MyPackage.class_0; with MyPackage.class_1; package MyPackage.RiA_Instances is -- Instance Declarations itsClass_0 : MyPackage.class_0.class_0_acc_t; type itsClass_1_card_t is new positive range 1..10; type itsClass_1_acc_lst_t is array(itsClass_1_card_t) of MyPackage.class_1.class_1_acc_t; itsClass_1 : itsClass_1_acc_lst_t; procedure Initialize_Relations; procedure Finalize_Relations; end MyPackage.RiA_Instances;
The Instances Package Specification.
with RiA_Default_Active; package body MyPackage.RiA_Instances is procedure Initialize_Relations is start_behavior_status : Boolean; begin -- Create the global instances itsClass_0 := new MyPackage.class_0.class_0_t; MyPackage.class_0.initialize(itsClass_0.all); for i in itsClass_1_card_t loop itsClass_1(i) := new MyPackage.class_1.class_1_t; end loop; -- Register the contexts RiA_Default_Active.register_context_class_0(itsClass_0.all); -- Start Behavior MyPackage.class_0.start_behavior(itClass_0.all, start_behavior_status); -- Hookup instances -- Ensure tat there is at least one statement null; end Initialize_Relations; procedure Finalize_Relations is begin MyPackage.class_0.Finalize(itsClass_0.all); end Finalize_Relations; end MyPackage.RiA_Instances;
The Instances Package Body.
If a link is created between the instances, the relation will be initialized as well in the Initialize_Relations procedure.
Only the Initialize_Relations procedure will be called from the auto-generated entrypoint.
Instance type
In object feature window, it is possible to define the object type. There are three different ways to define this type:
- user defined class: the type is selected among the classes defined in the model.
- implicit: the type is implicit. A new class will be auto generated. The class will have the name of the object. The instance name will be <<Object_Name>>_Instance.
- implicit and user defined: the type is implicit. User defines the package where the type is defined, in the property Ada_CG::Class::PackageName. A file will be generated to define this user defined Ada package.
Available properties
By modifying the following properties on the project, component, configuration, package or class level the user can get Rhapsody to use custom headers and footers instead of the default ones for generated files.
- Ada_CG.File.ImplementationFooter
- Ada_CG.File.ImplementationHeader
- Ada_CG.File.SpecificationFooter
- Ada_CG.File.SpecificationHeader
Defining custom header and footer at the component level
The usual Rhapsody inheritance rules apply for these properties, which means that you can refine your settings from the project level all the way down to the class.
These four properties are independent, which means that you can use a single project level setting for say the specification header and have different configuration level settings for the implementation header.
Those properties can also be updated at class level or at operation level (for separate operation). This can be useful to set change history log for example.
If the value of a keyword is a MultiLine, each new line (except the first one) starts with the value of the ADA_CG::Configuration::DescriptionBeginLine property; each line ends with the value of the ADA_CG::Configuration::DescriptionEndLine property.
Keyword substitution
Keyword based substitution is supported inside of these headers and footers.
The following keywords are supported:
- $ProjectName - The project name.
- $ComponentName - The component name.
- $ConfigurationName - The configuration name.
- $ModelElementName - The name of the element mapped to the file. If there is more than one, this is the name of the first element.
- $FullModelElementName - The name of the element mapped to the file, including the full path. If there is more than one, this is the name of the first element.
- $CodeGeneratedDate - The generation date.
- $CodeGeneratedTime - The generation time.
- $RhapsodyVersion - The version of Rhapsody that generated the file.
- $Login - The user who generated the file.
- $CodeGeneratedFileName - The name of the generated file.
- $FullCodeGeneratedFileName - The full file name.
- $Description - the description of the class or package
Note the following:
Keyword names can be written in parentheses. For example:
- $(Name)
- If the value of a keyword is a MultiLine, each new line (except the first one) starts with the value of the ADA_CG::Configuration::DescriptionBeginLine property; each line ends with the value of the ADA_CG::Configuration::DescriptionEndLine property.
.
Inserting keywords inside user-defined header and footer
--++ package default -- Component level Specification Header 1 -- sodius InheritedSetings DefaultComponent 6.0 Sodius -- Component level Specification Header 2 --++ class class_0 package class_0 is type class_0_t; type class_0_acc_t is access all class_0_t; type class_0_t is tagged null record; private end class_0; -- Component level Sepcification Footer 1 -- Component level Sepcification Footer 2
Example of generated code using user-defined header and footer
Script Evaluation
It is also possible to put script names inside of headers and footers so that they get evaluated at code generation time.
The name has to be framed following this convention [[scriptName]].
The script has to be applicable for the model element for which the header is being evaluated, otherwise an error message will be displayed.
Custom Makefiles
Introduction
Makefiles are usually generated by the Ada code generator. However they can also be created manually. This document describes all the features of custom makefiles, and gives some examples of makefile creation.
Features
Entry point
A makefile is built from 2 entry point properties
Ada_CG.<ENV>.MakeFileNameForExe
This property sets the name of the makefile. The extension must be inserted in this property.
Ada_CG.<ENV>. MakeFileContentForExe
This property is the file template. It can contain some text and some keywords. Keywords will be interpreted by CG.
There are some entry point properties to generate makefiles for executable project and for library projects. Different entry points will be used depending on the component property:
- Executable project
- MakeFileNameForExe
- MakeFileContentForExe
- Library project
- MakeFileNameForLib
- MakeFileContentForLib
If several files must be generated, then property names must be followed by a number from 1 to N. The CG will automatically scan all entry point properties:
- MakeFileNameForExe1
- MakeFileContentForExe1
- MakeFileNameForExe2
- MakeFileContentForExe2
2 additional entry points must be added for Green Hills compilers in order to generate entry point build files:
- FilenameEntrypointBuildFileContent
- EntrypointBuildFileContent
Keywords
Keywords are replaced by CG with some text. This text can also contain other keywords, which will be interpreted recursively.
Keywords are preceded by the character “$”
There are 2 kinds of keywords: property keyword and macro.
Property keyword
Property keywords can be any of the properties of the current environment. This keyword will be replaced by the content of the property. Its syntax is
$<property_name>
Example
Property MakeExtension String ".bat" Property MakeFileNameForExe String "makefile$MakeExtension”
In the second property, CG will replace $MakeExtension by the content of property Ada_CG.<ENV>.MakeExtension. The result will be:
makefile.bat
Macro
A Macro is a keyword which will be interpreted by CG to execute a script. Macros are recognized because they start with “$AdaCG”.
Some macros don’t begin by this prefix:
- $ComponentName
- $ProjectName
- $OMROOT
Creating new macro
If needed, users can add some new macros which will call Code Generator rule. The user must have knowledge of the RiA Code Generator rules in order to do this.
A property file must be created, which will make the mapping between the name of the macro and the script to call.
The file name must be:
<Rhp_install_dir>\share\\properties\MakeFileCommand.ini
Syntax to fill this file is
<Macro_Name>=<script_name> for a script defined in configuration level
<Macro_Name>=Project.<script_name> for a script defined in project level
This procedure is now obsolete. User do not need to create this new file. He can just call the rules by there name with "$" character in front of the name.
Standard Macros and property Keywords
This list of macros already known by the code generator:
Active_Component_Name | Returns the name of the active component | ||
Project_Name | Returns the project name | ||
Ada_CG_Build_Set | Returns the value of the field Build set in configuration settings | ||
AdaCGAnimationInclude | If animation is enabled, this macro will get property AnimationLibraries83Path, AnimationLibraries95Path or AnimationLibrariesNew95Path, depending on used FWK | ||
AdaCGAnimLib |
If animation is enabled, this macro will get property AnimationLibraries |
||
AdaCGBehavioralInclude | If animation is enabled, or if model needs Behavioral FWK, this macro will get property BehavioralLibraries83Path, BehavioralLibraries95Path or BehavioralLibrariesNew95Path | ||
AdaCGBehavioralLib | This macro gets properties BehavioralLibraries83Lib, BehavioralLibraries95Lib or BehavioralLibrariesNew95Lib depending on used FWK | ||
AdaCGBoochPath | If relations are used, this macro will get property Booch83Path or Booch95Path depending on used Booch component. | ||
AdaCGBoochFiles |
This macro gets some property depending on the following condition: if Uses_Relations_Include{ if Use_Booch_95_Components{ if Needs_Relations_Include_Bounded_Qualified{ get property Booch95RelationsIncludeBoundedQualified } if Needs_Relations_Include_Unbounded{ get property Booch95RelationsIncludeUnbounded} if Needs_Relations_Include_Unbounded_Not_Qualified{ get property Booch95RelationsIncludeUnboundedNotQualified} if Needs_Relations_Include_Unbounded_Qualified{ get property Booch95RelationsIncludeUnboundedQualified } } if Use_Booch_83_Components { if Needs_Relations_Include_Bounded_Qualified{ get property Booch83RelationsIncludeBoundedQualified} if Needs_Relations_Include_Unbounded{ get property Booch83RelationsIncludeUnbounded} if Needs_Relations_Include_Unbounded_Not_Qualified{ get property Booch83RelationsIncludeUnboundedNotQualified} if Needs_Relations_Include_Unbounded_Qualified{ get property Booch83RelationsIncludeUnboundedQualified } } } |
||
AdaCGContainerSet |
Returns a string depending on the kind of container set which should be used. The values are
|
||
AdaCGAdaPath |
This macro gets all generated folders. All folders are separated by “\n”. This macro uses the property AdaPathContent in order to format this list. See AdaPathContent description for more details. A similar marco do the same job, except that it does not include component and configurations dependencies : AdaCGGeneratedDir |
||
AdaCGGeneratedDir |
This macro gets all generated folders. All folders are separated by “\n”. This macro uses the property AdaPathContent in order to format this list. See AdaPathContent description for more details. A similar marco do the same job, except that it includes also component and configurations dependencies : AdaCGAdaPath |
||
AdaCGGenerateDirectory |
Returns the value of the property CG.Package. GenerateDirectory. The values are:
|
||
AdaCGAdaVersionSwitch | This macro will get property Ada83Switch, Ada95Switch, Ada2005Switch or Ada2012Switch, depending on Ada version used. If a model in Ada83 is animated, version switch will be forced to Ada95. | ||
AdaCGAdaVersionSwitchNoSpace | This macro is similar as AdaCGAdaVersionSwitchSpace, except that is does not add a white space before the content of the properties. | ||
AdaCGDebugSwitch | This macro generates the text of property CompileDebug if build set of configuration setting is set to debug. | ||
AdaCGAdditionalSources | This macro gets additional sources from configuration settings. It uses property AdditionalSourcesTemplate in order to format this text. See AdditionalSourcesTemplate description for more details. | ||
AdaCGUserIncludPath | This macro gets user include path from configuration settings. It uses property IncludePathTemplate in order to format this text. See IncludePathTemplate description for more details. | ||
AdaCGLibraries | This macro gets library from configuration settings. It uses property LibrariesTemplate in order to format this text. See LibrariesTemplate description for more details. | ||
AdaCGCompileSwitches | This macro gets compile switches from configuration settings | ||
AdaCGLinkSwitches | This macro gets link switches from configuration settings | ||
AdaCGUserIncludePath | This macro gets user include path from configuration settings. It uses property IncludePathTemplate in order to format this text. See IncludePathTemplate description for more details | ||
AdaCGFWK |
Returns a string depending of the kind of FWK which should be used. The value are
|
||
AdaCGGetInstrumentationType | Returns a string depending on the value of instrumentation type in configuration setting. | ||
AdaCGFileSpecList |
This Macro makes the list of all generated spec files. The spec file is added to the make file using the SpecTemplate property. This macro uses properties:
|
||
AdaCGFileBodyList |
This Macro makes the list of all generated body files This macro uses properties
|
||
GNAT_File_Mapping |
This macro generates mapping file list. It uses properties
It enables generating all generated file list for each elements in a single time. For example -- package_3::class_7 Start for spec ("package_3.class_7_Interface") use "class_7_Interface.ads"; for spec ("package_3.class_7_Port") use "class_7_Port.ads"; for body ("package_3.class_7_Port") use "class_7_Port.adb"; for spec ("package_3.class_7") use "class_7.ads"; for body ("package_3.class_7") use "class_7.adb"; -- package_3::class_7 End To get this result, the properties must have this values: SpecTemplate : for spec (\"$FileName\") use \"$SpecRelativeFilename\"; BodyTemplate : for body (\"$FileName\") use \"$SpecRelativeFilename\"; ProtectedStartTagFormat : -- $AdaCGRiAFullName Start ProtectedEndTagFormat : -- $AdaCGRiAFullName End |
||
AdaCGObjectAdaMakefile | This macro generates makefile for OBJECTADA compiler. “Compiler” property should be set to OBJECTADA. | ||
AdaCGGnatMakefile | This macro generates makefile for GNAT or GNATVxWorks compiler. “Compiler” property should be set to GNAT or GNATVxWorks. | ||
AdaCGGnatAdc | This macro generates gnat.adc file for GNAT or GNATVxWorks compiler. “Compiler” property should be set to GNAT or GNATVxWorks. | ||
AdaCGOptionalAdaPath | This is a rule which generates the code to define the variable ADA_INCLUDE_PATH | ||
AdaCGGnatchopCommands | Used to split AdaCGGnatMakeFile macro | ||
AdaCGCommands | This macro returns command line. It uses the property CompileCommand to get the template of the command | ||
AdaCGArchiveCommand | Used to split AdaCGGnatMakeFile macro | ||
AdaCGIDEName | Get value of IDEName tag of current configuration (for RiA in eclipse) | ||
AdaCGIDEProject | Get value of IDEProject tag of current configuration (for RiA in eclipse) | ||
AdaCGIDEWorkspace | Get value of IDEWorkspace tag of current configuration (for RiA in eclipse) | ||
AdaCGDefaultActiveClass | If default active class is need for FWK83, then this macro will get the property ActiveClassInclude. | ||
AdaCGFileList | This macro generates the list of generated spec files. It uses property FileTemplate in order to format this list. | ||
AdaCGOMROOTSingleSlashes | Generates OMROOT with back slashes. | ||
AdaCGOMROOTDoubleSlashes | Generates OMROOT with double back slashes | ||
AdaCGOMROOTForwardSlashes | Generates OMROOT with forward slashes. | ||
Component_Dependencies_With_Clause |
This macro is mainly used to generate GNAT gpr files This macro returns a list of with clause for each usage dependencies which are defined from the current component to another component. The template of the with clause is with <component_path>\<config_name><PathDelimiter><config_name>.gpr The component path can be given by the property Ada_CG.Configuration.TargetComponentPath. This enable to specify a specific path, to use the projet on a different target than the host one. The property Ada_CG.<environment>.PathDelimiter is also used to define the kind of path delimiter used by the target. |
||
Configuration_Dependencies_With_Clause |
This macro is mainly used to generate GNAT gpr files This macro returns a list of with clause for each usage dependencies which are defined from the current configuration to another configuration. The template of the with clause is with <configuration_path><PathDelimiter><config_name>.gpr The configuration path can be given by the property Ada_CG.Configuration.TargetConfigurationPath. This enable to specify a specific path, to use the projet on a different target than the host one. The property Ada_CG.<environment>.PathDelimiter is also used to define the kind of path delimiter used by the target. |
||
Makefile_OMROOT_With_Optional_Quotes | Returns the OMROOT value with Quotes. | ||
OMROOT | Generates OMROOT with back slashes. This string is quoted if the property QuoteOMROOT is set to “True”. |
This list of additional properties shows properties which are used by macro listed below:
CompileDebug | This property contains debug switches. Is used by AdaCGDebugSwitch macro. | |||
Ada83Switch Ada95Switch Ada2005Switch Ada2012Switch |
Those properties contain Ada version switches. | |||
AnimationLibraries83Path AnimationLibraries95Path AnimationLibrariesNew95Path |
Those properties contain Animation libraries path for each FWK | |||
BehavioralLibraries83Path BehavioralLibraries95Path BehavioralLibrariesNew95Path |
Those properties contain Behavioral libraries path for each FWK | |||
AnimationLibraries | This property gives the animation library located at <Rhp_Install_Dir>\ Share\LangC\Lib | |||
AdaPathContent |
This property contains patterns to be replaced. The format of this property is <”The_String_To_Replace”><”Is_Replaced_With”> For example <\”\\n\”><\”;\”> This will replace "\n" by ";" This property is used by macro AdaCGAdaPath and macro AdaCGGeneratedDir |
|||
Compiler |
This property sets the name of compiler. It is used to be able to reuse already existing rules with other environment. For example, if you create a new Environment called GNAT_1, and if you use the Macro AdaCGGnatMakefile, then some part of rules won’t work as expected because this new environment variable is unknown. So a new property is created to replace this environment name with compiler name. If a new compiler is created, this property is not useful, because it is unknown by CG. The values for this property should be:
|
|||
Booch83Path Booch95Path |
Those properties contain booch components path | |||
Booch95RelationsIncludeBoundedQualified Booch95RelationsIncludeUnbounded Booch95RelationsIncludeUnboundedNotQualified Booch95RelationsIncludeUnboundedQualified Booch83RelationsIncludeBoundedQualified Booch83RelationsIncludeUnbounded Booch83RelationsIncludeUnboundedNotQualified Booch83RelationsIncludeUnboundedQualified |
Those properties contain the list of booch component files needed for each kind of relation. | |||
AdditionalSourcesTemplate IncludePathTemplate LibrariesTemplate |
Those 3 properties allow replacing some string of configuration fields by some other string. Syntax is: [optional_string] <”The_String_To_Replace”><”Is_Replaced_With”> For example Property LibrariesTemplate MultiLine " -largs <\"\^\"><\" -l\"><\"\\n\"><\" -l\"><\"\\r\\n\"><\" -l\"><\",\"><\" -l\">" If user update configuration libraries with the string “lib1,lib2”, then it will be generated like this: -largs -llib1 -llib2 |
|||
BodyTemplate SpecTemplate |
Those properties are use to format the file list generated by Macros AdaCGFileBodyList and AdaCGFileBodyList. The following keyword can be used in this template:
For example for a class class_0 defined in default package those keywords will produce : ConfigurationRelativeFilename : .\Default\class_0 ConfigurationRelativeBodyFilename : .\Default\class_0.adb ConfigurationRelativeSpecFilename : .\Default\class_0.ads SpecRelativeFilename : class_0.ads BodyRelativeFilename : class_0.adb FileName : class_0 FullNameDashes : class_0("." Are replaced by "-") AdaCGRiAFullName : Default::class_0 |
|||
ProtectedStartTagFormat ProtectedEndTagFormat |
Those properties are used to format the file list generated by Macros AdaCGFileBodyList and AdaCGFileBodyList. They are used to add some tags in the list in order to help CG to add only new text in the file. The same keywords than properties BodyTemplate and SpecTemplate can be used. |
New environment creation
Create a new file SiteAda.prp
Add the following properties:
Subject Ada_CG Metaclass Configuration Property Environment Enum "GNAT,INTEGRITY,INTEGRITY5,MultiWin32,Multi4Win32,OBJECTADA,RAVEN_PPC,SPARK,GNATVxWorks,New_Env" "GNAT" end end
Copy an environment which is as close as possible to your new one, from sodius.prp to siteAda.prp. It must be copied just before the last “end” of the file.
Then modify entry points and add some new properties which will describe your new files.
Use cases
Custom makefiles can be created for several purposes. For example user needs to make a small modification of a current makefile. Or user wants to use a new compiler which is not supported by code generator. This chapter will describe how this can be done.
Create a new makefile for an unknown compiler.
Fast solution
Just add the text of the make file in the entry point property. This will generate always the same makefile. This solution can be used to make a quick test, but it cannot take into account all possible configurations.
Configurable solution
A new environment could be used for different kind of configuration. User can use different frameworks or Ada versions, or he can set animation or not. In order to take into account automatically those configurations, custom makefiles can be written with some macros which will automatically select the desired property depending on configuration properties.
Makefiles depend also on the model structure. A list of generated files or folders can be added to makefiles with the possibility to format the list.
Take into account Framework
If your model has events or statecharts, then a Framework must be used. Three different Frameworks can be used (FWK83, FWK95, NewFWK95), depending on property Ada_CG:Component:UseAdaFramework . Those Frameworks have been first generated and compiled for your compiler and your environment. You should know what their locations are. The path of your different Framework should be set in properties BehavioralLibraries83Path, BehavioralLibraries95Path and BehavioralLibrariesNew95Path, and you should invoke them by using macro AdaCGBehavioralInclude, which will select the correct property, depending on used Framework.
Take into account animation
If model is animated, you should add some libraries
C Animation libraries which are:
%OMROOT%\LangC\lib\AdaWinaomanim.lib %OMROOT%\LangC\lib\AdaWinoxfinst.lib %OMROOT%\LangC\lib\AdaWinomcomappl.lib
C libraries should be compiled as explaind in chapter "Manually rebuilding the animation C libraries".
Ada animation libraries path which are:
%OMROOT%\LangAda\aom for FWK 83.
%OMROOT%\LangAda\aom_95 for FWK95 and newFWK95.
You should use AdaCGAnimationInclude and AdaCGAnimationLib macros with there associated properties, in order to add animation facilities into makefiles.
Take into account relations
If your model contains relations which use booch components, then some booch components files must be added to your makefile.
Booch components are located in:
%OMROOT%\LangAda95\booch_ada_83\src\
%OMROOT%\LangAda95\booch_ada_95\src\
For each kind of relation, the Code generator uses a different set of Booch components.
Booch components 83
Relations_Include_Bounded_Qualified
map_simple_noncached_concurrent_bounded_managed_noniterator.ads
map_simple_noncached_concurrent_bounded_managed_noniterator.adb
Relations_Include_Unbounded
Storage_Manager_Concurrent.ads
Storage_Manager_Concurrent.adb
Relations_Include_Unbounded_Not_Qualified
list_single_unbounded_controlled.ads
list_single_unbounded_controlled.adb
list_utilities_single.ads
list_utilities_single.adb
list_search.ads
list_search.adb
Relations_Include_Unbounded_Qualified
Storage_Manager_Concurrent.ads
Storage_Manager_Concurrent.adb
map_simple_noncached_concurrent_unbounded_managed_noniterator.ads
map_simple_noncached_concurrent_unbounded_managed_noniterator.adb
Booch components 95
Relations_Include_Bounded_Qualified
bc.ads
bc-containers.ads
bc-containers.adb
bc-containers-maps.ads
bc-containers-maps.adb
bc-containers-maps-bounded.ads
bc-containers-maps-bounded.adb
bc-support.ads
bc-support-hash_tables.ads
bc-support-hash_tables.adb
bc-support-bounded_hash_tables.ads
bc-support-bounded_hash_tables.adb
Relations_Include_Unbounded
bc.ads
bc-support.ads
bc-support-standard_storage.ads
bc-support-unbounded.ads
bc-support-unbounded.adb
Relations_Include_Unbounded_Not_Qualified
bc.ads
bc-containers.ads
bc-containers.adb
bc-containers-collections.ads
bc-containers-collections.adb
bc-containers-collections-unbounded.ads
bc-containers-collections-unbounded.adb
Relations_Include_Unbounded_Qualified
bc.ads
bc-containers.ads
bc-containers.adb
bc-containers-maps.ads
bc-containers-maps.adb
bc-containers-maps-unbounded.ads
bc-containers-maps-unbounded.adb
You should use AdaCGBoochPath and AdaCGBoochFiles macro with their associated properties to add Booch component facilities into makefiles.
Modify the generated code of a current environment.
For example, you need to change the primaryTarget of your project for INTEGRITY5 environment. But this is hardcoded by the code generator. Instead of calling the macro AdaCGMulti4MakeFile, you will call a set of macros which are called by it. To do this you must use the Rules composer and generate code in debug mode. Open the debug hierarchy and find the script which is called by the macro.
Copy this script in a new property and change the script names into some custom names.
The script (get from Ada code generator rules) called by AdaCGMulti4MakeFile does this:
[#script] #!gbuild # Generated by Rhapsody ${self.MULTI_4_Top_Settings}${self.MULTI_4_Get_Library_Options}${self.MULTI_4_Get_Executable_Options} ${self.MULTI_4_Debug_Switches}${self.MULTI_4_Additional_Options}${self.Makefile_Compile_Switches}${self.Makefile_Link_Switches} # Generation directories settings -object_dir=obj --ada_info_dir info --ada_xref_dir xref ${self.MULTI_4_Ada_Path} ${self.MULTI_4_User_Include_Path}${self.MULTI_4_User_Libraries}${self.MULTI_4_RiA_Anim_Libs_Linker_Options} ${self.MULTI_4_Anim_And_Behavioral_Includes} Sources.gpj ${self.MULTI_4_Component_Type} ${self.MULTI_4_Entrypoints}[/#script]
The Macro AdaCGMulti4MakeFile should be replaced by the following text in property MakeFileContentForExe3
#!gbuild # Generated by Custom template primaryTarget=NEW_PRIMARY_TARGET_integrity.tgt [INTEGRITY Application] -o $ComponentName$ExeExtension # Target definition -bsp $BLDTarget -os_dir=$IntegrityRoot $BLDMainExecutableOptions $MULTI4DebugSwitches$MULTI4AdditionalOptions$MakefileCompileSwitches$MakefileLinkSwitches # Generation directories settings -object_dir=obj --ada_info_dir info --ada_xref_dir xref $MULTI4AdaPath $MULTI4UserIncludePath$MULTI4UserLibraries$MULTI4RiAAnimLibsLinkerOptions $MULTI4AnimAndBehavioralIncludes Sources.gpj [Project] Main$ComponentName$MakeExtension[program]
This new text will do the same job than the RulesComposer script. The names of the rules have been replaced by new Macros. The rule MULTI_4_Top_Settings has been replaced directly by some text, because this is the part of generation that we want to modify.
In order to enable the code generator to understand those new macros, a new initialization file must be updated, to create mappings between macros and rules.
Create the file:
<User_Share_Folder>\properties\MakeFileCommand.ini
In this file, you will map custom names to a script:
MULTI4DebugSwitches =Project.MULTI_4_Debug_Switches MULTI4AdditionalOptions =Project.MULTI_4_Additional_Options MakefileCompileSwitches =Project.Makefile_Compile_Switches MakefileLinkSwitches =Project.Makefile_Link_Switches MULTI4AdaPath =Project.MULTI_4_Ada_Path MULTI4UserIncludePath =Project.MULTI_4_User_Include_Path MULTI4UserLibraries =Project.MULTI_4_User_Libraries MULTI4RiAAnimLibsLinkerOptions =Project.MULTI_4_RiA_Anim_Libs_Linker_Options MULTI4AnimAndBehavioralIncludes =Project.MULTI_4_Anim_And_Behavioral_Includes
The prefix “Project.” means that this script is defined at project level. If this prefix is omitted, then the script must be defined at configuration level.
You should also take care of Framework location. You may have generated a new Framework for your new environment. So Framework location has changed. To do this, replace the Macro MULTI4AnimAndBehavioralIncludes by AdaCGBehavioralInclude. This macro will get information from properties BehavioralLibraries83Path, BehavioralLibraries95Path or BehavioralLibrariesNew95Path depending on Framework version.
Property BehavioralLibraries83Path should be for example:
--ada_elab_dirs '\'$AdaCGOMROOTForwardSlashes/LangAda83/oxf/NewEnv_sim800'' -L'$AdaCGOMROOTDoubleSlashes\\\\LangAda83\\\\oxf\\\\ NewEnv_sim800'
A new property “Compiler” should also be added and set to “INTEGRITY5” in order to be sure that the code generator will interpret the macro for the correct compiler.