How-To Migrate to Gaudi v21
Introduction
This twiki page is meant to provide instructions and, to some extent, the documentation for the changes between Gaudi v19/v20 and Gaudi v21.
Interfaces
Rationale
To extend a base component with the implementation of new interfaces, we have to provide an implementation of the method
IInterface::queryInterface
, which is almost always the same.
A typical case looks like:
class MySpecialization: public MyBase, virtual public IMyFeature {
public:
/// Constructor
MySpecialization();
/// Destructor
virtual ~MySpecialization();
/// Implementation from IInterface
virtual StatusCode queryInterface(const InterfaceID &riid, void **ppvInterface);
/// Implementation from IMyFeature
virtual StatusCode myMethod();
};
MySpecialization::MySpecialization():MyBase() {
// ... do something
}
MySpecialization::~MySpecialization() {
// ... do something
}
StatusCode MySpecialization::myMethod() {
// ... do something
return StatusCode::SUCCESS;
}
StatusCode MySpecialization::queryInterface(const InterfaceID &riid, void **ppvInterface) {
if (ppvInterface == 0) return StatusCode::FAILURE;
if (IMyFeature::interfaceId() == riid) { // sometimes is IID_IMyFeature
*ppvInterface = (IMyFeature*)this;
}
else {
return BaseClass::queryInterface(riid, ppvInterface);
}
addRef();
return StatusCode::SUCCESS;
}
The implementation of
queryInterface
is (usually) a switch-like chain of
if
statements falling back on the base-class implementation. The implementation of such a method can be automated, in fact the
AlgTool
base class has a generic implementation based on a run-time list of implemented interfaces, so, when the base class is
AlgTool
, the example above becomes:
class MySpecialization: public AlgTool, virtual public IMyFeature {
public:
/// Constructor
MySpecialization();
/// Destructor
virtual ~MySpecialization();
/// Implementation from IMyFeature
virtual StatusCode myMethod();
};
MySpecialization::MySpecialization():AlgTool() {
declareInterface<IMyFeature>(this);
}
MySpecialization::~MySpecialization() {
// ... do something
}
StatusCode MySpecialization::myMethod() {
// ... do something
return StatusCode::SUCCESS;
}
The code for
AlgTool
specializations is shorter and less error-prone. The only draw-back that has not been removed is that the declaration of the implemented interfaces happens in two places: the
.h
file and the
.cpp
one, so you have to edit the two files in a consistent way.
Another limitation of the current interface implementation is that it is not possible to have interfaces extending other interfaces. It is not a technical limitation (C++ allows it, of course), but it breaks the design principles of the framework. Each interface is identified by a numerical id (usually the a hash of the name) and a version (major and minor) that allow to tell if the component that we loaded from a library is compatible with the version of the interface that we used to compile our code. When an interface inherits from another and the base interface is changed, one should modify the version numbers of the derived interface, but it is simply impossible to keep track of those kind of chains.
Using some specially crafted templated helper classes, a dedicated
InterfaceId
class and some "Boost::mpl" (Meta Programming Library) constructs, it is possible to make the compiler generate all the mechanical code that is needed, simplifying the maintenance of the components and allowing non-trivial interface hierarchies.
Implementation
The implementation of the new interfaces infrastructure is based on the templated classes:
-
implements#<...>
- Used to write a class that does not have a concrete base class.
-
extends#<BASE, ...>
- Used to write a class that inherits from a concrete base class.
-
extend_interfaces#<...>
- Used to declare an interface that inherits from other interfaces.
Due to the absence of variadic template arguments in C++, the classes have different names depending on the number of interfaces passed as templates, e.g.:
-
implements1
-
extends2<AlgTool,ISpecial1,ISpecial2>
-
extend_interfaces1
The other key element of the new infrastructure is the preprocessor macro
DeclareInterfaceID
, which expands to some common code.
Interface
An interface declaration looks like:
class GAUDI_API IIncidentListener: virtual public extend_interfaces1<IInterface> {
public:
/// InterfaceID
DeclareInterfaceID(IIncidentListener,2,0);
/// Inform that a new incident has occurred
virtual void handle(const Incident&) = 0;
};
Line 1 make the interface extend
IInterface
(one can extend other interfaces, like
INamed
). Line 4 declare the interface name and version.
Component base class
class GAUDI_API Algorithm: virtual public implements3<IAlgorithm, IProperty, IStateful> {
public:
// ... no queryInterface
};
Line 1 shows how to use the the
implements#<...>
helper to implements 3 interfaces.
Extension of a component
class GAUDI_API ConversionSvc: public extends2<Service, IConversionSvc, IAddressCreator> {
public:
// ... no queryInterface
};
// Constructor
ConversionSvc::ConversionSvc(...): base_class(...) {}
The first template argument of
extends#<...>
is the component base class, for the rest, it works exactly as
implements#<...>
.
Line 6 shows how the constructor of the derived class must be written.
base_class
is a typedef to the actual base class (
extends#<...>
) that allows to avoid to repeat the list of interfaces in more than one place.
Migrating old code
Interfaces
- The interface must be modified to inherit from
extend_interfaces1
(or the version with the correct list of base interfaces).
- We must add the call to
DeclareInterfaceID
in the public
section, close to the declaration of the class.
- We must remove the static function
interfaceID
and all the references to the IID_
static variable.
Example:
IAlgorithm.
Component base class
- Make the class inherit from
implements#<...>
.
- Remove the implementation of
queryInterface
, addRef
and release
(unless non-trivial).
Extension of a component
- Make the class inherit from
extends#<...>
.
- Modify the constructor to call the constructor of
base_class
.
- Remove the implementation of
queryInterface
, addRef
and release
(unless non-trivial). In case of AlgTool specializations, remove the "declareInterface" lines from the constructor.
--
MarcoClemencic - 16 Feb 2009