int nType = ... ;
')
if ( boost :: is_base_of < ISettable, / * ... magically resolve type hidden here ... * / > :: value )
{
// Do something
}
else
{
// Do something else
}
int nObjectType = ResolveObjectType ( ... ) ;
boost :: shared_ptr < IObject > pObject = CreateObject ( nObjectType ) ;
template < class TObject >
class CFilter : public TObject
{
virtual bool FilterValue ( ... ) { ... } ;
} ;
(after all, if before some code reacted to an object of type A, now it still has to react to CFilter <A>, because the type has not changed in fact, just added new features).
int nType = ... ;
if ( boost :: is_base_of < ISettable, / * ... magically resolve type hidden here ... * / > :: value )
{
// Do something
}
else
{
// Do something else
}
//! Returns object type descriptor correspondent to TObject.
template < class TObject >
inline int MakeDescriptor ( ) ;
//! Calls rcFunctor with nTypeDescriptor.
template < class TFunctor >
inline typename Impl :: ResolveReturnType < TFunctor > :: type CallWithType ( const TFunctor & rcFunctor, int nTypeDescriptor ) ;
template < class TKind >
struct IsKindOfHelper
{
typedef bool
R ;
inline bool operator ( ) ( ... ) const
{
return false ;
}
inline bool operator ( ) ( TKind * ) const
{
return true ;
}
} ;
template < class TObject >
inline bool IsKindOf ( int nTypeDescriptor )
{
Return CallWithType ( IsKindOfHelper < TObject > ( ) , nTypeDescriptor ) ;
}
...
int nType = ... ;
if ( IsKindOf < ISettable > ( nType ) )
{
// Do something
}
else
{
// Do something else
}
template < class T, class U >
struct typelist
{
typedef T Head ;
typedef U Tail ;
} ;
typedef Loki :: Typelist < int , Loki :: Typelist < char , Loki :: Typelist < void , Loki :: NullType>>>
TMyList ;
// int MyInt;
Loki :: TypeAt < TMyList, 0 > :: Result MyInt ;
// char MyChar;
Loki :: TypeAt < TMyList, 1 > :: Result MyChar ;
int nIndexOfChar = Loki :: IndexOf < TMyList, char > :: value ;
List all known object types in a list of types. Then, the index of a particular type will be a type descriptor, but knowing the type descriptor, you can infer the type itself by looking at it in the list. The only problem is the following: for type inference by index, the index must be represented as a constant (compile-time values), i.e. we need to learn how to convert the values of numerical variables to the corresponding types of the type mpl :: int_ <# value #>
using namespace boost ;
namespace impl
{
//! A list of known object types.
/ ** In real world this structure constructed by templates. * /
typedef mpl :: list < TObjectType1, TObjectType2, TObjectType3 >
TKnownObjectTypes ;
//! Count of the known object types.
typedef mpl :: size < TKnownAtomTypes > :: type
TKnownObjectTypesCount ;
}
namespace impl
{
//! This metafunction returns an index of the TObject from the TKnownObjects.
/ ** If TObject is absent in TKnownObjects, returns -1 * /
template < class TObject >
struct MakeDescriptorImpl
: / * if * / mpl :: eval_if <
/ * find (TObject) == end * /
is_same <
typename mpl :: find < TKnownObjectTypes, TObject > :: type ,
mpl :: end < TKnownObjectTypes > :: type > ,
/ * then return -1 * /
mpl :: identity < mpl :: int_ < - 1 >> ,
/ * else return distance (begin, find (TObject)) * /
mpl :: apply <
mpl :: distance <
mpl :: begin < TKnownObjectTypes > :: type ,
mpl :: find < TKnownObjectTypes, _ >> ,
TObject >> :: type
{
} ;
//! Helps to call TFunctor with TObjectType *
template < class TFunctor >
struct CallWithObjectTypeHelperPointerBased
{
public :
// typename ResolveReturnType <TFunctor> :: type evalutes to TFunctor :: R if
// TFunctor :: R typedef is present, otherwise, it is to void.
typedef typename ResolveReturnType < TFunctor > :: type
R ;
protected :
const TFunctor &
m_rcFunctor ;
public :
CallWithObjectTypeHelperPointerBased ( const TFunctor & rcFunctor )
: m_rcFunctor ( rcFunctor )
{
}
//! This function is called by CallWithInt (...).
template < class TIndex >
R operator ( ) ( TIndex ) const
{
// Find object type by index
typedef typename mpl :: at < TKnownObjectTypes, TIndex > :: type
TObject ;
// Call functor with pointer to real object type
return m_rcFunctor ( ( TObject * ) NULL ) ;
}
//! This function is called by CallWithInt (...).
R operator ( ) ( mpl :: void_ ) const
{
// The descriptor is broken, call functor with special value
return m_rcFunctor ( mpl :: void_ ( ) ) ;
}
} ;
}
//! Returns Object type descriptor correspondent to TObject.
template < class TObject >
inline int MakeDescriptor ( )
{
// Attempt to make Object type description for unknown Object type!
BOOST_STATIC_ASSERT ( Impl :: MakeDescriptorImpl < TObject > :: value ! = - 1 ) ;
// Return descriptor, this is actually compile time generated constant.
return Impl :: MakeDescriptorImpl < TObject > :: value ;
}
//! Calls rcFunctor with TObject * correspondent to nObjectTypeDescriptor.
template < class TFunctor >
inline typename Impl :: ResolveReturnType < TFunctor > :: type CallWithType ( const TFunctor & rcFunctor, int nObjectTypeDescriptor )
{
// Call with int will call
// Impl :: CallWithObjectTypeHelperPointerBased <TFunctor> (rcFunctor)
// functor with mpl :: int_ <N> () argument, where N is compile time constant
// correspondent to the value of nObjectTypeDescriptor.
// If nObjectTypeDescriptor <0 || nObjectTypeDescriptor> = TKnownObjectTypesCount
// functor will be used with mpl :: void_ (), indicating that type descriptor is broken.
return Impl :: CallWithInt < mpl :: int_ < 0 > , TKnownObjectTypesCount > (
Impl :: CallWithObjectTypeHelperPointerBased < TFunctor > ( rcFunctor ) ,
nObjectTypeDescriptor ) ;
}
Example: 42 is converted to mpl :: int_ <42>
It remains for us to perform the last (most difficult) step: write a function that will call a functor with the type mpl :: int_ <N> (), where N corresponds to the value stored inside the run-time variable. This is perhaps the most difficult part, because it is this that translates runtime values into types.
We make a function with a switch, say one hundred elements, this function should call any functor with the value mpl :: int <N>, where N corresponds to the value of the variable passed to the function. If we are asked to transform a different interval, we simply do additional manipulations: offset and separation into bins. So for example, if we are asked to convert a number in the range 56 ... 156 to a type, you just need to subtract 56 times from the variable that was passed to us, and then, after the conversion to type, add 56 (but to the type!). If we are asked to convert a number from the interval 200..400, we must first divide it into sections "100 each", then calculate the number of the section and the offset inside the section.
int nObjectType = ResolveObjectType ( ... ) ;
switch ( nObjectType )
{
case 1 :
return new TObject1 ( ) ;
case 2 :
return new TObject2 ( ) ;
case 3 :
return new TObject3 ( ) ;
case 4 :
return new TObject4 ( ) ;
/ * ... 100+ more cases goes here * /
default :
return NULL ;
} ;
//! This wrapper allows to determine real object type.
template < class TBase, class TObject = TBase >
struct CTypeWrapper
: public TBase
{
//! Returns type descriptor correspondent to this instance.
virtual int TypeDescriptor ( ) const
{
Return MakeDescriptor < TObject > ( ) ;
}
} ;
//! Helps to create objects. Actually this is a functor, called with Object type.
class CreateObjectHelper
{
public :
// Return type
typedef IObject *
R ;
private :
template < class TBase, class TObject >
inline TBase * MakeObject ( ) const
{
return new CObjectTypeWrapper < TBase, TObject > ( ) ;
}
public :
//! Generic case
template < class TObject >
inline TObject * operator ( ) ( TObject * , ... ) const
{
return MakeObject < TObject, TObject > ( ) ;
}
inline IObject * operator ( ) ( boost :: mpl :: void_ ) const
{
assert ( ! "Type Descriptor Is Broken! Mustn't be here!" ) ;
return NULL ;
}
public :
// Special case for objects, derived from IObjectType1
template < class TObject >
IObject * operator ( ) ( TObject * , IObjectType1 * ) const
{
// ...
}
// Special case for objects, derived from IObjectType2
template < class TObject >
IObject * operator ( ) ( TObject * , IObjectType2 * ) const
{
// ...
}
} ;
...
int nObjectType = ResolveObjectType ( ... ) ;
return ObjectTraits :: CallDoublePointerBasedFunctorWithObjectType (
CreateObjectHelper ( ) ,
nObjectType ) ;
template < class TKind >
struct IsKindOfHelper
{
typedef bool
R ;
inline bool operator ( ) ( ... ) const
{
return false ;
}
inline bool operator ( ) ( TKind * ) const
{
return true ;
}
} ;
template < class TObject >
inline bool IsKindOf ( int nTypeDescriptor )
{
Return CallWithType ( IsKindOfHelper < TObject > ( ) , nTypeDescriptor ) ;
}
...
// TypeDescriptor is a virtual object type.
// Implementation shown in previous example (this function by hands)
if ( IsKindOf < ISettable > ( this - > TypeDescriptor ( ) ) )
{
// Do something
}
else
{
// Do something else
}
int ApplySomeWrapper ( int nType )
{
bool bShouldBeWrapperApplied = ... ;
if ( bShouldBeWrapperApplied )
{
// IsWrapperApplicable converts nType to the real type TObject using CallWithType
// and calls WrapperTraits :: CSomeWrapper :: IsApplicable <TObject> :: value metafunction
if ( IsWrapperApplicable < WrapperTraits :: CSomeWrapper > ( nType ) )
{
// IsWrapperApplicable converts nType to the real type TObject using CallWithType,
// calls WrapperTraits :: CSomeWrapper :: MakeWrappedType <TObject> :: type metafunction
// in order to resolve wrapped type, then calls MakeDescriptor for this type.
return MakeWrappedType < WrapperTraits :: CSomeWrapper > ( nType ) ;
}
}
return nType ;
}
Source: https://habr.com/ru/post/108196/
All Articles