Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Is this void* cast safe?

Reply
Thread Tools

Is this void* cast safe?

 
 
jason.cipriani@gmail.com
Guest
Posts: n/a
 
      06-08-2008
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
 
Reply With Quote
 
 
 
 
Frank Birbacher
Guest
Posts: n/a
 
      06-08-2008
Hi!

http://www.velocityreviews.com/forums/(E-Mail Removed) schrieb:
> // '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());
> }
> };


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<T> which 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<T> appropriately?


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 T> class A : BaseDoIt {
public:
void doit () {
std:rintf("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<BaseDoIt> helper;
};

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

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

This is even safer. And cleaner! And faster!

Frank
 
Reply With Quote
 
 
 
 
joseph cook
Guest
Posts: n/a
 
      06-09-2008
> // non template class
> struct B
> {
> * * * * // but template ctor:
> * * * * template<typename T>
> * * * * B();
> * * * * void call_doit();
> private:
> * * * * const std::auto_ptr<BaseDoIt> helper;
>
> };
>
> 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)?
 
Reply With Quote
 
Eric Pruneau
Guest
Posts: n/a
 
      06-09-2008

<(E-Mail Removed)> a écrit dans le message de news:
12dfde48-1052-415a-9623-8c21766e278e...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 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_;
> };


union {
A<int>* a_int;
A<float>* a_float;
}


> // 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);


if (t == tInt) a_int = new A<int>;
else if(t == tFloat) a_float = 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);


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


 
Reply With Quote
 
Eric Pruneau
Guest
Posts: n/a
 
      06-09-2008

"Frank Birbacher" <(E-Mail Removed)> a écrit dans le message de news:
(E-Mail Removed)...
> Hi!
>
> (E-Mail Removed) schrieb:
>> // '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());
>> }
>> };

>
> 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<T> which 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<T> appropriately?

>
> 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 T> class A : BaseDoIt {
> public:
> void doit () {
> std:rintf("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<BaseDoIt> helper;
> };
>
> 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...


 
Reply With Quote
 
Gianni Mariani
Guest
Posts: n/a
 
      06-09-2008
(E-Mail Removed) 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.
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      06-09-2008
On Jun 9, 4:00 am, "Eric Pruneau" <(E-Mail Removed)> wrote:
> <(E-Mail Removed)> a écrit dans le message de news:
> (E-Mail Removed)...


> > 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:(E-Mail Removed)
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
 
Reply With Quote
 
Frank Birbacher
Guest
Posts: n/a
 
      06-09-2008
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
 
Reply With Quote
 
Frank Birbacher
Guest
Posts: n/a
 
      06-09-2008
joseph cook schrieb:
>> // non template class
>> struct B
>> {
>> // but template ctor:
>> template<typename T>
>> B();
>> void call_doit();
>> private:
>> const std::auto_ptr<BaseDoIt> helper;
>>
>> };
>>
>> 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(; //using int
B b2(8.f); //using float

**OR:**

B::B(std::auto_ptr<BaseDoIt> inst)
: 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
 
Reply With Quote
 
Thomas J. Gritzan
Guest
Posts: n/a
 
      06-09-2008
Gianni Mariani schrieb:
> (E-Mail Removed) 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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Is the result of valid dynamic cast always equal to the result ofcorrespondent static cast? Pavel C++ 7 09-18-2010 11:35 PM
error C2440: 'return' : cannot convert from 'const char *' to 'const unsigned short *' Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast Abhijit Bhadra C++ 2 12-01-2004 04:43 PM
malloc - to cast or not to cast, that is the question... EvilRix C Programming 8 02-14-2004 12:08 PM
to cast or not to cast malloc ? MSG C Programming 38 02-10-2004 03:13 PM
how to cast system.intptr to struct BestNews ASP .Net 1 09-03-2003 09:04 AM



Advertisments