468,315 Members | 1,447 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,315 developers. It's quick & easy.

Is this void* cast safe?

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 Tclass A {
public:
void doit () {
std::printf("A<%s>::doit()\n", typeid(T).name());
}
};

// 'B' is not a template class...
class B {
public:
// ... but uses A<Tbelow 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 Tvoid call_doit_helper ();
Type t_;
void *va_;
};

// constructor constructs appropriate A<Tbut 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<Tdestructor 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 Tvoid 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<Tmember, but uses T based on some value
passed to the constructor at run time. It stores the A<Tit creates
in the member variable "void *va_" and then, later, casts back to an
A<Tin the call_doit_helper<Tfunction.

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<Tappropriately?

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
Jun 27 '08 #1
9 1785
Hi!

ja************@gmail.com schrieb:
// 'A' is just a run-of-the-mill template class.
template <class Tclass A {
public:
void doit () {
std::printf("A<%s>::doit()\n", typeid(T).name());
}
};
and
void B::call_doit () {

if (t_ == tInt)
call_doit_helper<int>();
else if (t_ == tFloat)
call_doit_helper<float>();
else
assert(0);

}
This looks like a manual virtual function dispatch. I suggest you create
a base class for all A<Twhich has a virtual destructor and an abstract
doit() function.

1. There's nothing weird going on here with that cast, right?
Everything should work out OK?
Yes, all is ok.
2. In B's destructor, will that destroy the A<Tappropriately?
Yes, correct dtor.
3. Is static_cast<what I want to be using here (as opposed to
reinterpret_cast, I guess)?
Yes, use static_cast.
Again maybe this is a silly question but I've been confusing the heck
out of myself staring at this code all day.
How about:

struct BaseDoIt
{
virtual ~BaseDoIt() {}
virtual void doit() =0;
};

//copied from your code, but BaseDoIt added:
template <class Tclass A : BaseDoIt {
public:
void doit () {
std::printf("A<%s>::doit()\n", typeid(T).name());
}
};

// non template class
struct B
{
// but template ctor:
template<typename T>
B();
void call_doit();
private:
const std::auto_ptr<BaseDoIthelper;
};

template<typename T>
B::B()
: helper(new A<T>())
{
}

void B::call_doit()
{
helper->doit();
}

This is even safer. And cleaner! And faster!

Frank
Jun 27 '08 #2
// non template class
struct B
{
* * * * // but template ctor:
* * * * template<typename T>
* * * * B();
* * * * void call_doit();
private:
* * * * const std::auto_ptr<BaseDoIthelper;

};

template<typename T>
B::B()
* * * * : helper(new A<T>())
{

}
Template C'tor in a non template class? How would one possibly create
an object of this type (type B)?
Jun 27 '08 #3

<ja************@gmail.coma écrit dans le message de news:
12**********************************...oglegroups.com...
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.

I you dont like the void*, you can always use a union.

=== BEGIN PROGRAM ===

#include <cassert>
#include <cstdio>
#include <typeinfo>

// 'A' is just a run-of-the-mill template class.
template <class Tclass A {
public:
void doit () {
std::printf("A<%s>::doit()\n", typeid(T).name());
}
};

// 'B' is not a template class...
class B {
public:
// ... but uses A<Tbelow 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 Tvoid call_doit_helper ();
Type t_;
void *va_;
};
union {
A<int>* a_int;
A<float>* a_float;
}

// constructor constructs appropriate A<Tbut 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);
if (t == tInt) a_int = new A<int>;
else if(t == tFloat) a_float = new A<float>;
else assert(0);
}

// destructor calls appropriate A<Tdestructor 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);
here you can get rid of the static cast.:

if (t_ == tInt)
delete a_int;
else if (t_ == tFloat)
delete a_float;
else
assert(0);

using a union is probably a little clearer, you get rid of the cast and the
void* without having to make big changes to your code wich is a good thing
if your code is complex and if it is already working.

--------------

Eric Pruneau
Jun 27 '08 #4

"Frank Birbacher" <bl************@gmx.neta écrit dans le message de news:
6b*************@mid.dfncis.de...
Hi!

ja************@gmail.com schrieb:
>// 'A' is just a run-of-the-mill template class.
template <class Tclass A {
public:
void doit () {
std::printf("A<%s>::doit()\n", typeid(T).name());
}
};

and
>void B::call_doit () {

if (t_ == tInt)
call_doit_helper<int>();
else if (t_ == tFloat)
call_doit_helper<float>();
else
assert(0);

}

This looks like a manual virtual function dispatch. I suggest you create a
base class for all A<Twhich has a virtual destructor and an abstract
doit() function.

>1. There's nothing weird going on here with that cast, right?
Everything should work out OK?

Yes, all is ok.
>2. In B's destructor, will that destroy the A<Tappropriately?

Yes, correct dtor.
>3. Is static_cast<what I want to be using here (as opposed to
reinterpret_cast, I guess)?

Yes, use static_cast.
>Again maybe this is a silly question but I've been confusing the heck
out of myself staring at this code all day.

How about:

struct BaseDoIt
{
virtual ~BaseDoIt() {}
virtual void doit() =0;
};

//copied from your code, but BaseDoIt added:
template <class Tclass A : BaseDoIt {
public:
void doit () {
std::printf("A<%s>::doit()\n", typeid(T).name());
}
};

// non template class
struct B
{
// but template ctor:
template<typename T>
B();
void call_doit();
private:
const std::auto_ptr<BaseDoIthelper;
};

template<typename T>
B::B()
: helper(new A<T>())
{
}

void B::call_doit()
{
helper->doit();
}

This is even safer. And cleaner! And faster!

Frank
helper is a member function of B???

Can't call a member function in the constructor initializer list...
Jun 27 '08 #5
ja************@gmail.com wrote:
....
>
Again maybe this is a silly question but I've been confusing the heck
out of myself staring at this code all day.
Apart from Frank's suggestion, you could use an Any object.

This is the austria "Any" class.
http://austria.svn.sourceforge.net/v....h?view=markup

Boost has a similar class.
Jun 27 '08 #6
On Jun 9, 4:00 am, "Eric Pruneau" <eric.prun...@cgocable.cawrote:
<jason.cipri...@gmail.coma écrit dans le message de news:
12dfde48-1052-415a-9623-8c21766e2...@a1g2000hsb.googlegroups.com...
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.
I you dont like the void*, you can always use a union.
Which is a lot cleaner when the number of types is limited.
Also, when an enum is involved, typically, a switch is
preferable to a string of if/else if's.

But of course Frank's solution is even cleaner, since it avoids
the type switching entirely.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #7
Hi!

Eric Pruneau schrieb:
helper is a member function of B???

Can't call a member function in the constructor initializer list...
"helper" is a member variable of type "const std::auto_ptr<BaseDoIt>".
It is constructed in the initializer list. Const values must be
constructed there.

Frank
Jun 27 '08 #8
joseph cook schrieb:
>// non template class
struct B
{
// but template ctor:
template<typename T>
B();
void call_doit();
private:
const std::auto_ptr<BaseDoIthelper;

};

template<typename T>
B::B()
: helper(new A<T>())
{

}

Template C'tor in a non template class? How would one possibly create
an object of this type (type B)?
Hmm, you are right. There is not way to explicitly specify the
instantiation to be used and there is no type deduction.

Ok, two "workarounds":

template<typename T>
B::B(T) //use parameter to deduce the type
: helper(new A<T>())
{}

Here you would supply an object of the type you need:
B b1(8); //using int
B b2(8.f); //using float

**OR:**

B::B(std::auto_ptr<BaseDoItinst)
: helper(inst)
{}

Here you would need to supply an instance of BaseDoIt your self:
B b1(new A<int>);
B b2(new A<float>);

Iff B was copyable you could do:

template<typename T>
B B::create()
{
return B(new A<T>);
}

Frank
Jun 27 '08 #9
Gianni Mariani schrieb:
ja************@gmail.com wrote:
...
>>
Again maybe this is a silly question but I've been confusing the heck
out of myself staring at this code all day.

Apart from Frank's suggestion, you could use an Any object.

This is the austria "Any" class.
http://austria.svn.sourceforge.net/v....h?view=markup
...or you could use a variant class (typesafe union), like
boost::variant, which might be a good idea if you have a limited set of
types.

In case of boost::variant, you then could use the visitor pattern to
operate on the object, instead of a chain of if statements.

I think that a 'typesafe union' is more useful than a 'typesafe void*',
because it is easier to work with a fixed set of types. Your code can
only have a limited number of if/else if statements anyway.

--
Thomas
Jun 27 '08 #10

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

5 posts views Thread by johny smith | last post: by
188 posts views Thread by infobahn | last post: by
34 posts views Thread by Alawna | last post: by
6 posts views Thread by rouble | last post: by
27 posts views Thread by Erik de Castro Lopo | last post: by
9 posts views Thread by Alex Vinokur | last post: by
5 posts views Thread by WittyGuy | last post: by
35 posts views Thread by =?utf-8?b?QXNiasO4cm4gU8OmYsO4?= | last post: by
7 posts views Thread by Charles Zhang | last post: by
reply views Thread by NPC403 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.