Advanceret template programmering ( double dispatch )
Hej eksperter !Jeg er i gang med at konstruere et template-bibliotek til script-sprog. Disse vil indeholde forskellige typer, men da jeg gerne vil sikre bedst mulig udførselshastighed, har jeg besluttet at for foruddefinerede typer skal evaulering af udtryk med disse, bruge parametriserede templates.
Men jeg er løbet lidt ind i et "problem", da jeg gik i gang med at lave funktionaliteten for binære operationer (to operander, ikke bit operationer). Mit problem optræder i funktionen GSL_ExpressionT<T>::Unify - hvor jeg vha. makroen TEST_TYPE bruger RTTI til at afgøre typen af anden operand. Jeg er interesseret i at høre om der er nogen her der kender (kan finde frem til) en måde at udføre denne funktionalitet vha. klassisk double dispatch ? At komme fra dynamisk typeinformation til statisk typeinformation er kernen i dette problem, er dette muligt ?
Unify gør jo jobbet glimrende for første operand, men for begge operander ?
Derudover er jeg interesseret i kommentarer til denne fremgangsmåde + evt. problemer jeg kunne støde på senere hen.
Jeg har barberet min kode ned, til relativt simpel eksempel (2 typer og 4 operationer) - men samtidig holdt den så den kan oversættes.
// Hold on - this definitely isn't Kansas any more :
#include <iostream>
#include <cassert>
enum binary_operator { binop_add, binop_sub,
binop_mul, binop_div };
template<binary_operator OP, typename T>
class eval_functor
{
public:
T operator() ( T const& op1, T const& op2 )
{ assert(false); }
};
#define EVAL_FUNCTOR(OPID,OP) \
template<typename T> \
class eval_functor<OPID,T> \
{ \
public: \
T operator() (T const & op1, T const & op2 ) \
{ return op1 OP op2; } \
}
EVAL_FUNCTOR(binop_add,+);
EVAL_FUNCTOR(binop_sub,-);
EVAL_FUNCTOR(binop_mul,*);
EVAL_FUNCTOR(binop_div,/);
class GSL_Expression
{
public:
GSL_Expression()
{ }
virtual ~GSL_Expression()
{ }
virtual GSL_Expression * Unify( GSL_Expression * second, binary_operator op ) = 0;
virtual void Output( std::ostream& ) const = 0;
};
std::ostream & operator << ( std::ostream & os, GSL_Expression const & e )
{
e.Output(os);
return os;
}
// Forward declaration :
template<typename T1, typename T2>
class Unification;
#define TEST_TYPE(type) \
GSL_ExpressionT<type> * test ## type = \
dynamic_cast<GSL_ExpressionT<type> *>(second); \
if( test ## type ) \
{ \
Unification<T,type> unifier; \
return unifier( this, test ## type, op ); \
}
template<typename T>
class GSL_ExpressionT : public GSL_Expression
{
public:
GSL_ExpressionT()
: GSL_Expression()
{ }
virtual ~GSL_ExpressionT()
{ }
virtual GSL_Expression * Unify( GSL_Expression * second, binary_operator op )
{
TEST_TYPE(int)
TEST_TYPE(double)
assert(false);
}
virtual void Output( std::ostream& os) const
{
os << this->evaluate();
}
virtual T evaluate( void ) const = 0;
};
template<typename T>
class GSL_PrimaryValue : public GSL_ExpressionT<T>
{
private:
T primary;
public:
GSL_PrimaryValue( const T& init )
: GSL_ExpressionT<T>(), primary(init)
{ }
virtual ~GSL_PrimaryValue()
{ }
virtual T evaluate( void ) const
{ return primary; }
};
template<typename T, binary_operator OP>
class GSL_Binary : public GSL_ExpressionT<T>
{
private:
GSL_ExpressionT<T> * op1, * op2;
public:
GSL_Binary( GSL_ExpressionT<T> * lhs,
GSL_ExpressionT<T> * rhs )
: GSL_ExpressionT<T>(), op1(lhs), op2(rhs)
{ }
virtual ~GSL_Binary()
{
delete op1;
delete op2;
}
virtual T evaluate( void ) const
{
eval_functor<OP,T> f;
return f( op1->evaluate(), op2->evaluate() );
}
};
template<typename T_DESTINATION, typename T_SOURCE>
class GSL_Cast : public GSL_ExpressionT<T_DESTINATION>
{
private:
GSL_ExpressionT<T_SOURCE> * op;
public:
GSL_Cast( GSL_ExpressionT<T_SOURCE> * src )
: GSL_ExpressionT<T_DESTINATION>(), op(src)
{ }
virtual ~GSL_Cast()
{
delete op;
}
virtual T_DESTINATION evaluate( void ) const
{
return static_cast<T_DESTINATION>(op->evaluate());
}
};
template<typename T_DESTINATION, typename T_SOURCE>
class cast_functor
{
public:
GSL_ExpressionT<T_DESTINATION> * operator() ( GSL_ExpressionT<T_SOURCE> * src )
{
return new GSL_Cast<T_DESTINATION,T_SOURCE>( src );
}
};
// Partial specialization to ensure that only needed cast objects are created :
template<typename T>
class cast_functor<T,T>
{
public:
GSL_ExpressionT<T> * operator() ( GSL_ExpressionT<T> * src )
{
return src;
}
};
template<typename T1, typename T2>
class Promotion;
template<typename T>
class Promotion<T,T>
{
public:
typedef T ResultType;
};
#define MK_PROMOTION(T1,T2,Tr) \
template<> class Promotion<T1, T2> { \
public: \
typedef Tr ResultType; \
}; \
\
template<> class Promotion<T2, T1> { \
public: \
typedef Tr ResultType; \
};
MK_PROMOTION( int , double, double )
template<typename T1, typename T2>
class Unification
{
private :
typedef typename Promotion<T1,T2>::ResultType ResultType;
public :
GSL_Expression * operator() ( GSL_ExpressionT<T1> *init_lhs,
GSL_ExpressionT<T2> *init_rhs,
binary_operator op)
{
cast_functor<ResultType,T1> lhs_f;
GSL_ExpressionT<ResultType> *lhs = lhs_f( init_lhs );
cast_functor<ResultType,T2> rhs_f;
GSL_ExpressionT<ResultType> *rhs = rhs_f( init_rhs );
switch( op )
{
case binop_add : return new GSL_Binary<ResultType,binop_add> (lhs,rhs);
case binop_sub : return new GSL_Binary<ResultType,binop_sub> (lhs,rhs);
case binop_mul : return new GSL_Binary<ResultType,binop_mul> (lhs,rhs);
case binop_div : return new GSL_Binary<ResultType,binop_div> (lhs,rhs);
default :
break;
}
assert(false);
}
};
inline GSL_Expression * Binary_Expr( GSL_Expression * lhs,
GSL_Expression * rhs,
binary_operator op)
{
return lhs->Unify(rhs,op);
}
int main( int argc, char *argv[])
{
GSL_Expression * e1 = Binary_Expr(new GSL_PrimaryValue<int>(5),
new GSL_PrimaryValue<int>(8),
binop_mul);
std::cout << "The result is : " << *e1 << std::endl;
GSL_Expression * e2 = Binary_Expr(new GSL_PrimaryValue<double>(2.1),
new GSL_PrimaryValue<double>(4.7),
binop_add);
std::cout << "The result is : " << *e2 << std::endl;
GSL_Expression * e3 = Binary_Expr(e1,e2,binop_sub);
std::cout << "The result is : " << *e3 << std::endl;
return 0;
}