15.3 Delegation-based Interface Implementation
The delegation-based interface implementation approach is an alternative to using inheritance when implementing CORBA objects. This approach is used when the overhead of inheritance is too high or cannot be used. For example, due to the invasive nature of inheritance, implementing objects using existing legacy code might be impossible if inheritance for some global class were required. Instead, delegation can be used to solve these types of problems. Delegation is a more natural fit doing object implementations when the Process-Entity design pattern is used. In this pattern, the Process object would delegate operations onto one or more entity objects.
In the delegation-based approach, the implementation does not
inherit from a skeleton class. Instead, the implementation can be
coded as required for the application, and a wrapper object will
delegate upcalls to that implementation. This “wrapper
object,” called a tie, is generated by the IDL
compiler, along with the same skeleton class used for the
inheritance approach. The generated tie class is partially
opaque to the programmer, though, like the skeleton, it provides a
method corresponding to each OMG IDL operation for the associated
interface. The name of the generated tie class is the same
as the generated skeleton class with the addition that the string
_tie
is appended to the end of the class name.
An instance of the tie
class is the servant, not
the C++ object being delegated to by the tie object, that is passed
as the argument to the operations that require a
Servant
argument. It should also be noted that the
tied object has no access to the _this( )
operation,
nor should it access data members directly.
A type-safe tie class is implemented using C++ templates. The following code snippet explains a tie class generated from the Derived interface in the previous OMG IDL example.
// C++
template <class T>
class POA_A_tie : public POA_A {
public:
POA_A_tie(T& t)
: _ptr(&t), _poa(PortableServer::POA::_nil()), _rel(0) {}
POA_A_tie(T& t, PortableServer::POA_ptr poa)
: _ptr(&t), _poa(PortableServer::POA::_duplicate(poa)), _rel(0) {}
POA_A_tie(T* tp, CORBA::Boolean release = 1)
: _ptr(tp), _poa(PortableServer::POA::_nil()), _rel(release) {}
POA_A_tie(T* tp, PortableServer::POA_ptr poa, CORBA::Boolean release = 1)
: _ptr(tp), _poa(PortableServer::POA::_duplicate(poa)), _rel(release) {}
~POA_A_tie()
{ CORBA::release(_poa);
if (_rel) delete _ptr;
}
// tie-specific functions
T* _tied_object () {return _ptr;}
void _tied_object(T& obj)
{ if (_rel) delete _ptr;
_ptr = &obj;
_rel = 0;
}
void _tied_object(T* obj, CORBA::Boolean release = 1)
{ if (_rel) delete _ptr;
_ptr = obj;
_rel = release;
}
CORBA::Boolean _is_owner() { return _rel; }
void _is_owner (CORBA::Boolean b) { _rel = b; }
// IDL operations*************************************
CORBA::Short op1 ()
{
return _ptr->op1 ();
}
void op2 (CORBA::Long val)
{
_ptr->op2 (val);
}
// ***************************************************
// override ServantBase operations
PortableServer::POA_ptr _default_POA()
{
if (!CORBA::is_nil(_poa))
{
return _poa;
}
else {
#ifdef WIN32
return ServantBase::_default_POA();
#else
return PortableServer::ServantBase::_default_POA();
#endif
}
}
private:
T* _ptr;
PortableServer::POA_ptr _poa;
CORBA::Boolean _rel;
// copy and assignment not allowed
POA_A_tie (const POA_A_tie<T> &);
void operator=(const POA_A_tie<T> &);
};
This class definition is a template generated by the IDL compiler. You typically use it by first getting a pointer to the legacy class and then instantiating the tie class with that pointer. For example:
Old::Legacy * legacy = new Old::Legacy( oid);
POA_A_tie<Old::Legacy> * A_servant_ptr =
new POA_A_tie<Old::Legacy>( legacy );
As you can see, the tie class contains definitions for the op1
and op2 operations of the interface that assume that the legacy
class has operations with the same signatures as those given in the
IDL. If this is the case, you can use the tie class file as is,
letting it delegate exactly. It is more likely, however, that the
legacy class will not have identical signatures or you may have to
do more than a single function call. In that case, it is your job
to replace the code for op1 and op2 in this generated code. The
code for each operation typically makes invocations on the legacy
class using the tie class variable _ptr
, which
contains the pointer to the legacy class. For example, you might
change the following lines:
CORBA::Short op1 () {return _ptr->op1 (); }
void op2 (CORBA::Long val) {_ptr->op2 (val); }
to the following:
CORBA::Short op1 ()
{
return _ptr->op37 ();
}
void op2 (CORBA::Long val)
{
CORBA::Long temp;
temp = val + 15;
_ptr->lookup(val, temp, 43);
}
An instance of this template class performs the task of
delegation. When the template is instantiated with a class type
that provides the operation of the Derived
interface,
then the POA_Derived_tie
class will delegate all
operations to an instance of that implementation class. A reference
or pointer to the actual implementation object is passed to the
appropriate tie constructor when an instance of the
POA_Derived_tie
class is created. When a request is
invoked on it, the tie servant will just delegate the request by
calling the corresponding method on the implementation class.
The use of templates for tie classes allows the application developer to provide specializations for some or all of the template’s operations for a given instantiation of the template. This allows the application to use legacy classes for tied object types, where the operation signatures of the tied object will differ from that of the tie class.
Parent topic: Server Side Mapping