J
jason.cipriani
All right, I'm in this weird situation that's hard to explain but I've
put together a small example program that represents it. Please bear
with the fact that some of the stuff in the example seems useless,
it's from a much more complex situation.
The example program is confusing for me to think about and to look at,
and even though I'm -pretty- sure it's safe, I just want to make sure.
The thing in question is casting from a class pointer to a void * then
back to a class pointer again.
=== BEGIN PROGRAM ===
#include <cassert>
#include <cstdio>
#include <typeinfo>
// 'A' is just a run-of-the-mill template class.
template <class T> class A {
public:
void doit () {
std:rintf("A<%s>::doit()\n", typeid(T).name());
}
};
// 'B' is not a template class...
class B {
public:
// ... but uses A<T> below based on enum passed to constructor:
enum Type { tInt, tFloat };
B (Type t);
~B ();
void call_doit ();
private:
// call_doit_helper is templated to access va_ as an A<T>.
template <class T> void call_doit_helper ();
Type t_;
void *va_;
};
// constructor constructs appropriate A<T> but stored in void*:
B::B (Type t) : t_(t) {
if (t == tInt)
va_ = new A<int>;
else if (t == tFloat)
va_ = new A<float>;
else
assert(0);
}
// destructor calls appropriate A<T> destructor as well
B::~B () {
if (t_ == tInt)
delete static_cast<A<int> *>(va_);
else if (t_ == tFloat)
delete static_cast<A<float> *>(va_);
else
assert(0);
}
// call_doit_helper is specialized for valid types in the actual code
this is
// all taken from; but in this example the default template works
fine:
template <class T> void B::call_doit_helper () {
A<T> *a = static_cast<A<T> *>(va_);
a->doit();
}
// in the actual code this is taken from this function does much more
but
// in this example it just calls call_doit_helper depending on the
type.
void B::call_doit () {
if (t_ == tInt)
call_doit_helper<int>();
else if (t_ == tFloat)
call_doit_helper<float>();
else
assert(0);
}
int main () {
B bi(B::tInt);
B bf(B::tFloat);
bi.call_doit();
bf.call_doit();
}
=== END PROGRAM ===
In that program there is a template class A and a non-template class
B. The B class has an A<T> member, but uses T based on some value
passed to the constructor at run time. It stores the A<T> it creates
in the member variable "void *va_" and then, later, casts back to an
A<T> in the call_doit_helper<T> function.
1. There's nothing weird going on here with that cast, right?
Everything should work out OK?
2. In B's destructor, will that destroy the A<T> appropriately?
3. Is static_cast<> what I want to be using here (as opposed to
reinterpret_cast, I guess)?
Again maybe this is a silly question but I've been confusing the heck
out of myself staring at this code all day.
Thanks,
Jason
put together a small example program that represents it. Please bear
with the fact that some of the stuff in the example seems useless,
it's from a much more complex situation.
The example program is confusing for me to think about and to look at,
and even though I'm -pretty- sure it's safe, I just want to make sure.
The thing in question is casting from a class pointer to a void * then
back to a class pointer again.
=== BEGIN PROGRAM ===
#include <cassert>
#include <cstdio>
#include <typeinfo>
// 'A' is just a run-of-the-mill template class.
template <class T> class A {
public:
void doit () {
std:rintf("A<%s>::doit()\n", typeid(T).name());
}
};
// 'B' is not a template class...
class B {
public:
// ... but uses A<T> below based on enum passed to constructor:
enum Type { tInt, tFloat };
B (Type t);
~B ();
void call_doit ();
private:
// call_doit_helper is templated to access va_ as an A<T>.
template <class T> void call_doit_helper ();
Type t_;
void *va_;
};
// constructor constructs appropriate A<T> but stored in void*:
B::B (Type t) : t_(t) {
if (t == tInt)
va_ = new A<int>;
else if (t == tFloat)
va_ = new A<float>;
else
assert(0);
}
// destructor calls appropriate A<T> destructor as well
B::~B () {
if (t_ == tInt)
delete static_cast<A<int> *>(va_);
else if (t_ == tFloat)
delete static_cast<A<float> *>(va_);
else
assert(0);
}
// call_doit_helper is specialized for valid types in the actual code
this is
// all taken from; but in this example the default template works
fine:
template <class T> void B::call_doit_helper () {
A<T> *a = static_cast<A<T> *>(va_);
a->doit();
}
// in the actual code this is taken from this function does much more
but
// in this example it just calls call_doit_helper depending on the
type.
void B::call_doit () {
if (t_ == tInt)
call_doit_helper<int>();
else if (t_ == tFloat)
call_doit_helper<float>();
else
assert(0);
}
int main () {
B bi(B::tInt);
B bf(B::tFloat);
bi.call_doit();
bf.call_doit();
}
=== END PROGRAM ===
In that program there is a template class A and a non-template class
B. The B class has an A<T> member, but uses T based on some value
passed to the constructor at run time. It stores the A<T> it creates
in the member variable "void *va_" and then, later, casts back to an
A<T> in the call_doit_helper<T> function.
1. There's nothing weird going on here with that cast, right?
Everything should work out OK?
2. In B's destructor, will that destroy the A<T> appropriately?
3. Is static_cast<> what I want to be using here (as opposed to
reinterpret_cast, I guess)?
Again maybe this is a silly question but I've been confusing the heck
out of myself staring at this code all day.
Thanks,
Jason