3D PLM Enterprise Architecture |
Middleware Abstraction - Object Modeler |
Creating ComponentsEnabling your clients to instantiate your usable and scalable objects |
Technical Article |
AbstractThis article explains how to provide client applications with tools to create your implementation objects. It is a programmer's guide (how to) to show on simple examples the way an the code to write to create a global creation function, implement the CATICreateInstance interface, or a factory. |
Components are the atoms that build an application. A component is a piece of executable code that you can't modify, but that you can use by means of the interfaces it exposes, that hides its implementation details, that can be replaced, even at run time, by another component exposing the same interfaces and performing the same job, and that ensures upward compatibility to client applications using it.
Compared to many past and often present monolithic applications, CATIA offers, since 1988 and its Version 3, an application programming interface that enables you to create applications, such as IUA commands, GII interactive functions, and batch applications. Since 1992 and CATIA Version 4, the same openness happens with data, and you can add your own application data to the CATIA data. Even if CATIA itself was a monolithic application, the applications and the data you added match partially the component definition, since you can reuse your applications on newer CATIA releases without the need of rebuilding them, and your application data without migrating it.
With the help of the object-oriented technology, data and software make a whole named object, and components are now made of objects. Objects provide the features required by components:
We'll see in this article what is a CAA V5 component made up of, how you can create them as CAA V5 component providers, and how you can use and extend them as client application programmers.
[Top]
Client application programmers have usually a logical view of CAA V5 components and see a CAA V5 component as an object exposing interfaces. On the opposite, the component supplier has a detailed physical view of the CAA V5 component, that is, he/she knows which C++ classes make up the component, how these C++ classes are assembled to build the component, and which interface is implemented by which class.
[Top]
As a client application programmer, you often need to create components and manipulate them using the interfaces they expose, whatever the way and the complexity of their internal structure. For example, when you need to create a circle, you call the circle component factory which creates a circle instance and returns a pointer to the CATICircle interface. Then you need to move the circle, and you call QueryInterface to get a pointer to the CATIMove interface for this circle component instance. Then you may need to draw it, or modify its display attributes. Whenever you need to do something to this circle, you ask for a pointer to the appropriate interface if you haven't one already, and you use this pointer to call the methods exposed by this interface for this component.
Your view of the component you manipulate is a logical view that makes you see the component as if it were simple and self-contained.
[Top]
As a component supplier, your view of the component may be quite different. You may want to take advantage of the code factorization capabilities offered by CAA V5, and make full use of extensions. This makes it possible for you to design and code a single application component like a circle as several C++ classes, one being the circle main implementation class, and the others extension classes of this main implementation class, each of these classes implementing one or several interfaces through which the client application programmer will manipulate the circle, seeing it as a simple and single component.
For example, the classes making up the circle could be designed as shown on Fig 2.
According to this design, up to four classes can be instantiated when the client application programmer manipulates a circle, but everything happens as if only one component were manipulated.
[Top]
The CAA V5 object model modularity and scalability is offered through extensions. This is a very powerful means to build CAA V5 components from C++ objects and to supply software updates without implying to rebuild the client applications.
An extension to a main implementation class is a separate class that is attached to the main implementation class and which extends it to implement additional interfaces that this main implementation class does not implement itself. Extension common usage is to share the implementation of interfaces between several components, or to implement a new interface which did not exist at the time the framework was released and which was added after. This helps a lot for modularity and scalability.
Extensions are C++ classes which must have the CATBaseUnknown
class among its base class, and that must directly OM-derive from CATBaseUnknown
.
They can be of the following types:
The extension class and the class it is an extension of are declared in the
extension source file using the macro CATImplementClass
. The link
between the extension and the interface it implements is managed through the
dictionary. The shared library which contains the code for the extension is also
included.
The class that implement interfaces as an extension of a component is created
automatically when a pointer to an interface the extension implements is
requested by QueryInterface
. So you don't need to worry about a
factory of global function to create extensions.
Note that the extension is not a recursive concept. There are no extensions of extensions.
[Top]
A data extension is a C++ class that contains data members and methods. Suppose that our object MyObject is already used by numerous clients and that we want to add data that it doesn't hold already. If we supply a new version of our object which implements an additional interface for this data, the clients will be very happy to get a new functionality, but may become unhappy when we'll tell them to rebuild their applications, even if they don't use this interface. To overcome this problem, we can use a data extension as follows:
The new interface CATIData is implemented by the separate C++ class MyDataExtension with methods to access the data and data members to store it. There is no need to rebuild the client applications when you deliver the data extension, since the MyObject class has not changed from these applications standpoint. Only client applications that want to take advantage of the update need of course to be modified and rebuilt.
Lifecycle: A call to QueryInterface
against the object
for an interface implemented by a data extension of this object creates the data
extension instance, or uses the existing one. The data extension is deleted only
when the object of which it is an extension is also deleted, that is when all
the reference counts of the object and of all its extensions reach 0.
The idl file for CATIData is as follows:
// IDL encoded interface #pragma ID CATIData "DEC:7db286f1-218d-0000-0280020a86000000" interface CATIData : CATBaseUnknown { #pragma PROPERTY Length HRESULT get_Length(out int oLength); HRESULT put_Length(in int iLength); }; |
The resulting header file CATIData.h
is as follows:
// C++ generated interface class header file #include "CATBaseUnknown.h" extern IID IID_CATIData; class CATIData : CATBaseUnknown { CATDeclareInterface; public : virtual HRESULT __stdcall get_Length(int * oLength) = 0; virtual HRESULT __stdcall set_Length(int iLength) = 0; }; |
The resulting source file CATIData.cpp
is as follows:
// C++ generated interface class source file #include "CATIData.h" IID IID_CATIData = { 0x7db286f1, 0x218d, 0x0000, {0x02, 0x80, 0x02, 0x0a, 0x86, 0x00, 0x00, 0x00} }; CATImplementInterface(CATIData, CATBaseUnknown); |
The MyDataExtension class implements this interface:
#include "CATBaseUnknown.h" class MyDataExtension : public CATBaseUnknown { CATDeclareClass; public : MyDataExtension(); virtual ~MyDataExtension(); virtual HRESULT __stdcall get_Length(int * oLength); virtual HRESULT __stdcall set_Length(int iLength); private : int _Length; }; |
The CAAEDataExtension class source file is as follows:
#include "CAAEDataExtension.h" CATImplementClass(CAAEDataExtension, // Extension class name DataExtension, // Data extension CATBaseUnknown, // Always OM-derive extensions from CATBaseUnknown MyObject); // Main class of the extended component #include "TIE_CATIData.h" TIE_CATIData(CAAEDataExtension); CAAEDataExtension::CAAEDataExtension() {} CAAEDataExtension::~CAAEDataExtension() {} HRESULT CAAEDataExtension::get_Length(int * oLength) { oLength = _Length; return S_OK; } HRESULT CAAEDataExtension::set_Length(int iLength) { _Length = iLength; return S_OK; } |
The CATImplementClass
macro states that the class
CAAEDataExtensionis a data extension and that extends the MyObject class. The
third argument that declares the component from which the current one OM-derives
makes sense only for component main classes and must always be set to CATBaseUnknown
with an extension.
[Top]
A code extension is an extension that contains only methods, and no data. At run-time, for a given code extension, only one instance of this code extension exists, whatever the number of existing objects this code extension extends.
As for data extensions, there is no need to rebuild client applications when the code extension is supplied.
You code a code extension as a data extension, and just use CodeExtension
instead of DataExtension
as the second parameter of the CATImplementClass
macro in the code extension class source file, as follows:
... CATImplementClass(CAAECodeExtension, CodeExtension, CATBaseUnknown, MyObject); ... |
Lifecycle: Once created for a given object instance, the code extension is never deleted and is used for all the instances of the same object.
[Top]
Sharing an extension makes several classes implement the interfaces the extension itself implements, and not only one class as shown in the above sections about extensions. This is another step in code factorization and reusability, and in code scalability.
There are two ways for sharing extensions. The first way is to let the extension itself declare of which classes it is an extension. This is very handy if you have existing classes for which you want to add an additional behavior by means of the interfaces implemented in the extension. You don't need to rebuild the existing classes, just supply the extension. The second way is to declare the extension support in the classes that are extended by the extension. This is very handy if you have an existing extension and if you want to declare it as an extension of new classes. You don't need to rebuild the extension, just supply the new classes.
Declare that an extension class is an extension for several classes in the extension class source file (.cpp) as follows:
CATBeginImplementClass(MyExtensionClassName, // Begin declaration DataExtension, CATBaseUnknown, TheFirstClassIExtend); CATAddClassExtension(TheSecondClassIExtend); CATAddClassExtension(TheThirdClassIExtend); ... CATAddClassExtension(TheLastClassIExtend); CATEndImplementClass(MyExtensionClassName); // End declaration |
The CATBeginImplementClass
macro replaces the CATImplementClass
macro. The CATAddClassExtension
macro is used to add a class to the
extended classes, and the CATEndImplementClass
macro ends the
declaration sequence for the concerned extension.
You declare an existing extension in a new class in this class source file (.cpp) as follows:
CATSupportImplementation(ExtensionClassName, MyClassName, ImplementedInterface); |
The CATSupportImplementation
macro allows addition of existing
extensions incrementally to new classes.
[Top]
In the code example above, when implementing CATIData, the following lines were used:
... CATImplementClass(CAAEDataExtension, // Extension class name DataExtension, // Data extension CATBaseUnknown, // Base component - Always OM-derive TIE extensions from CATBaseUnknown MyObject); // Implementation class of the extended component #include "TIE_CATIData.h" TIE_CATIData(CAAEDataExtension); ... |
Let's detail them:
CATImplementClass
macro is used in conjunction with the CATDeclareClass
macro in the class header file to express that the class is part of a CAA V5
Object Modeler component. Its arguments read as follows:
Implementation
,
DataExtension
, and CodeExtension
Implementation
type classesDataExtension
type classes that implement one
interface using the BOADataExtension
type classes that implement all their
interfaces using TIEsCodeExtension
type classesTIE_CATIData
macro codeTIE_CATIData
macro that actually creates the
code for the TIE class. The macro parameter is the name of the class that
implements the interface. The TIE class is instantiated and a pointer to it
is returned by QueryInterface
when the component is asked for CATIData.
The TIE is an intermediate class that ties the client application and the
component at run time without any link at build time.The TIE is the technology promoted by CAA V5 to handle interfaces at run time. Nevertheless, in some scenarios where performance is critical, instantiating an intermediate object may be costly. For example, if a component is instantiated thousands of times and if a pointer to a given interface is requested against each component instance, thousands of TIE objects are created and may lead to memory allocation problems. To avoid this, CAA V5 proposes an alternate solution: the BOA.
BOA stands for Basic Object Adapter. The BOA technology makes QueryInterface
return a pointer to the class that implements the interface rather than a
pointer to an intermediate class. The BOA saves a class instance per component
in scenarios such as the one described above. Even if a pointer to the
implementing class is returned, it is returned as an interface pointer, and
there is not more coupling between interface and implementation than with the
TIE. The client application does not know which class implements the interface,
has no build time link with its header file or module, and can thus only call
the methods exposed by this interface.
To implement CATIData using the BOA instead of the TIE:
#include "CATIData.h" class MyDataExtension : public CATIData { CATDeclareClass; public : MyDataExtension(); virtual ~MyDataExtension(); virtual HRESULT __stdcall get_Length(int * oLength); virtual HRESULT __stdcall set_Length(int iLength); private : int _Length; }; |
CATImplementClass
and the CATImplementBOA
macros.
... CATImplementClass(CAAEDataExtension, // Extension class name DataExtension, // Data extension CATIData, // Always OM-derive BOA extensions from the BOA implemented interface MyObject); // Implementation class of the extended component CATImplementBOA(CATIData, CAAEDataExtension); ... |
CATImplementClass
must be set to
the implemented interface from which the extension class derivesCATImplementBOA
macro replaces the TIE_CATIData
macro. Its arguments are the BOA-implemented interface and the extension
class name respectively.Note that since the class implementing the interface using the BOA must
derive from the interface, and that CAA V5 does not support multi-inheritance, a
given class can BOA-implement only one interface, the others being
TIE-implemented. So carefully choose the appropriate interface to BOA-implement
if your class implements several interfaces. In addition, BOA is not available
with CodeExtension
type classes.
CAA V5 BOA candidate interfaces are the U4 and U5 labelled interfaces [3], that is, the interfaces you implement and that V5 calls onto your components. U4 means that you implement the interface by deriving a V5 supplied adapter class and override a part of the methods. U5 means that you write the code for all the methods of the interface. While there is no BOA restriction with U5 interfaces, pay attention with U4 interfaces. A U4 interface must be BOA-enabled, that is, its adapter must derive from it, otherwise BOA cannot be used for that interface. Among the U4 interfaces, some are BOA-enabled, and some are not. To know whether a U4 interface is BOA-enabled, check BOA information section in the interface documentation.
[Top]
A component is an object that implements interfaces. It is seen as a single object from the component user standpoint, but can be implemented as several C++ objects.
An extension is an object that is part of a component to provide it a given behavior by implementing one or several interfaces. The extension is not seen from the client application, but is handled using a pointer to an interface it implements. An extension is also the means to add behavior to a component without modifying already existing client applications.
The TIE and the BOA are the CAA V5 technologies to implement interfaces. A class can implement many interfaces using the TIE, but only one of them can be implemented using the BOA.
[Top]
[1] | Creating Interfaces |
[2] | Using Components |
[3] | CAA V5 Resource Exposition, Usage, Deprecation, and Stability |
[Top] |
Version: 2 [Oct 2003] | BOA added |
Version: 1 [Jan 2000] | Document created |
[Top] |
Copyright © 2000, Dassault Systèmes. All rights reserved.