I want to define a function template<typename T> T constCast(const ScriptVar_t& s);. Depending on T, I want to have different definitions. (ScriptVar_t is a class but details are not important here in this context.)
The conditions on T aren’t as simple as specific types, they are all somewhat more complicated static boolean expressions. I.e. I have a list of expressions ext1..extN and for each, I have a definition of that function. And I want to have them checked in that order and the definition of the first matching expression should be used. If all of them fail, I want to get a compiler error.
Right now, I just have 2 definitions and my code looks like this (this is a full test case, the relevant code is marked):
#include <boost/type_traits.hpp>
enum {
SVT_INT,
SVT_FLOAT,
SVT_BASEOBJ,
SVT_CUSTOMVAR
};
struct BaseObject {};
struct CustomVar {};
template<typename T> struct GetType;
template<> struct GetType<int> { static const int value = SVT_INT; };
template<> struct GetType<float> { static const int value = SVT_FLOAT; };
template<> struct GetType<BaseObject> { static const int value = SVT_BASEOBJ; };
template<bool> struct GetType_BaseCustomVar;
template<> struct GetType_BaseCustomVar<true> {
struct Type { static const int value = SVT_CUSTOMVAR; };
};
template<typename T> struct GetType : GetType_BaseCustomVar<boost::is_base_of<CustomVar,T>::value>::Type {};
struct ScriptVar_t;
template<typename T> T CastScriptVarConst(const ScriptVar_t& s);
struct ScriptVar_t {
operator int() const { return 0; }
operator float() const { return 0.0f; }
operator BaseObject() const { return BaseObject(); }
template<typename T> T* as() const { return NULL; }
template <typename T> T castConst() const { return CastScriptVarConst<T>(*this); }
};
// *** relevant code starts here
template<typename T> T CastScriptVarConst(const ScriptVar_t& s);
template<bool> struct CastScriptVar1;
template<typename T> struct CastScriptVar1_IsSimpleType {
static const bool value = GetType<T>::value < SVT_BASEOBJ;
};
template<> struct CastScriptVar1<true> {
template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) { return (T) s; }
};
template<bool> struct CastScriptVar2;
template<typename T> struct CastScriptVar2_IsCustomVar {
static const bool value = boost::is_base_of<CustomVar,T>::value;
};
template<> struct CastScriptVar2<true> {
template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) { return *s.as<T>(); }
};
template<> struct CastScriptVar1<false> {
template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) {
return CastScriptVar2<CastScriptVar2_IsCustomVar<T>::value>::castConst(s, T());
}
};
template<typename T> T CastScriptVarConst(const ScriptVar_t& s) {
return CastScriptVar1<CastScriptVar1_IsSimpleType<T>::value>::castConst(s, T());
}
int main() {
ScriptVar_t v;
v.castConst<int>();
v.castConst<CustomVar>();
}
I came up with this after a few tries until it works.
(As you can see from the code, the two expressions are GetType<T>::value < SVT_BASEOBJ and boost::is_base_of<CustomVar,T>::value. If both are false, the compiler should throw an error. But this is only an example for my question.)
I wonder if there is a somewhat more clean solution for this code.
For reference, I am playing with it here. And right now, I have again a somewhat different solution to all the other solutions here.
If I understood correctly, I’d use a look-up table for the
cast-functors and a meta-function to calculate the offset into the
table.
An alternative would be to use a type based look-up table consisting
of tags and functors.
pick_castwould then choose the right taginstead of an
int. This might be easier to read if the decisiontable becomes large.