Templated upcast function
I have a templated function that attempts to upcast a type to a specified base class.
If TBase is a base of T then a simple upcast is performed.
If TBase is not a base of T then an exception is thrown.
The downsides of the current inplementation is that at all possible types of T must be polymorphic for dynamic_cast to compile, plus the fact that I have to use dynamic_cast at all.
In theory, the dynamic_cast is unecessary as any types which pass the std::is_base_of test are, by definition, simple upcasts, but the compiler will check the syntax of that branch even though it will never be taken for a non-TBase derived T.
Any ideas on how this could be improved?
BTW I can't use any C++11 features that can't be easily reverse engineered in C++03.
Code:
template <typename TBase, typename T>
TBase& DoUpCast(T& t)
{
if (std::is_base_of<TBase, T>::value)
{
return dynamic_cast<TBase&>(t);
}
else
{
throw VariantNotABaseTypeException();
}
}
Re: Templated upcast function
I think I'm close to a solution, but for a (for the moment) puzzling compiler error.
Code:
template <typename TBase, typename T, const bool IsBase>
struct DoIt;
template <typename TBase, typename T>
struct DoIt<TBase, T, true>
{
TBase& operator()(T& t)
{
return t;
}
};
template <typename TBase, typename T>
struct DoIt<TBase, T, false>
{
TBase& operator()(T& t)
{
throw VariantNotABaseTypeException();
}
};
template <typename TBase, typename T>
TBase& DoUpCast(T& t)
{
DoIt<TBase, T, std::is_base_of<TBase, T>::value> doIt;
return doIt(T& t); //<<<< 'T' Illegal use of this type as an identifier.
}
Re: Templated upcast function
Re: Templated upcast function
I think something like this might be simpler
Code:
namespace details{
template <typename TBase, typename T>
TBase& DoUpCast(T& t,std::true_type)
{
return dynamic_cast<TBase&>(t);
}
template <typename TBase, typename T>
TBase& DoUpCast(T& t,std::false_type)
{
throw VariantNotABaseTypeException();
}
}
template <typename TBase, typename T>
TBase& DoUpCast(T& t)
{
return details::DoUpCast(t,std::is_base_of<TBase, T>::type());
}
Re: Templated upcast function
Am I missing something? Why wait until runtime to tell the programmer they have done something wrong? Why not just fail at compile time?
Code:
template <bool canUpCast>
struct FailIfCannotUpCast;
template <>
struct FailIfCannotUpCast<true>{};
template <typename TBase, typename T>
TBase& DoUpCast(T& t)
{
FailIfCannotUpCast<std::is_base_of<TBase, T>::value>();
returnt t;
}
Re: Templated upcast function
Quote:
Originally Posted by
2kaud
Do'h!!
I must have been a bit overtired when I wrote that bit :blush:
Re: Templated upcast function
Quote:
Originally Posted by
PredicateNormative
Am I missing something? Why wait until runtime to tell the programmer they have done something wrong? Why not just fail at compile time?
The class it's used in has a variable number of template types. The unused template types default to 'null type' place holders.
If the check was at compile time then the placeholders fail the test.
I will probably look at seeing whether further template specialisations can convert this to a purely compile time check.
Re: Templated upcast function
Quote:
Originally Posted by
JohnW@Wessex
I will probably look at seeing whether further template specialisations can convert this to a purely compile time check.
I strongly recommend you review this design.
If you end up having to turn a type safe upcast into an unsafe downcast for implementation reasons only, something is seriously wrong.
Why is everybody so hellbent on circumventing type safety, one of the major assets of C++? Beats me.
All this time consuming fiddling back and forth to "outsmart the compiler". All this brittle and difficult to maintain code being produced that not even the original author understands. If you cannot do it right in a simple and straightforward fashion, don't do it at all.
Re: Templated upcast function
The type checks are now all compile time. It was a fairly simple exercise in the end. :)
Quote:
Originally Posted by
razzle
I strongly recommend you review this design.
If you end up having to turn a type safe upcast into an unsafe downcast for implementation reasons only, something is seriously wrong.
Where are the downcasts? The only casts available to the user are upcasts and they are now all checked at compile time.
Attempting to cast the variant to a class that is not a base of the polymorphic types that can be stored by the variant is a compile time error.
There are some 'reinterpret' casts internally, but they are soley to access the internal storage buffer. The user has no access to these.
The aim of the design was to create a heterogeneous type that enforced virtually all of its type checks at compile time.
The only runtime exception that can occur is when a type is requested that is not currently contained in the variant, but is supported by the Variant.
Quote:
Why is everybody so hellbent on circumventing one of the major assets of C++ all the time
I'm using the assets of C++ to make sure that they cannot be circimvented by the user of the Variant class.
Quote:
If you cannot do it right in a simple and straightforward fashion, don't do it at all.
Gosh, I wish all engineering problems were that easy!
Quote:
All this brittle and difficult to maintain code being produced
If anyone were to see the internals of the class then I pretty sure they'd find there's nothing that would tax even a half decent C++ programmer who knew their templates.
The Variant class is a thin wrapper around a generic storage area to produce a 'super' union with all of the type safety that a union hasn't.
Re: Templated upcast function
why do you need any code at all ?
dynamic_cast<>()
already does what you want. if you upcast it'll typecheck and throw if it's not valid. (you need to enable RTTI)
Re: Templated upcast function
Quote:
Originally Posted by
OReubens
why do you need any code at all ?
dynamic_cast<>()
already does what you want.
But that generates a runtime error.
With the Variant template class it's a compile time error.
Re: Templated upcast function
Code:
class Base {};
class NotBase {};
class Derived1 : public Base {};
class Derived2 : public Base {};
Variant<Derived1, Derived2> values;
Base& base = values; // Compile fail. Ambiguous conversion.
Base& base = values.UpCast<Base>(); // Compile pass.
Base& base = values.UpCast<NotBase>(); // Compile fail. Variants are not derived from NotBase.
Code:
Variant<Derived1, Derived2, int> values;
Base& base = values2.UpCast<Base>(); // Compile fail. 'int' is not derived from Base.