PHWinfo banniere

Titres
PORTAIL ANNUAIRE ARTICLES COMPARATEUR HÉBERGEURS DEVIS FORUMS RÉDUCTEUR D'URL
Précédent   PHWinfo > Autres forums > Forum Programmation & Conception > comp.lang.cplus > Is this void* cast safe?
S'inscrire FAQ Membres Recherche Messages du jour Marquer les forums comme lus
Is this void* cast safe?

Réponse
 
LinkBack Outils de la discussion
Vieux 08/06/2008, 20h20   #1
jason.cipriani@gmail.com
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut 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 T> class 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<T> below based on enum passed to constructor:
enum Type { tInt, tFloat };
B (Type t);
~B ();
void call_doit ();
private:
// call_doit_er is templated to access va_ as an A<T>.
template <class T> void call_doit_er ();
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_er 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_er () {

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_er depending on the
type.
void B::call_doit () {

if (t_ == tInt)
call_doit_er<int>();
else if (t_ == tFloat)
call_doit_er<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_er<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
  Réponse avec citation
Vieux 08/06/2008, 20h41   #2
Frank Birbacher
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?

Hi!

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


and

> void B::call_doit () {
>
> if (t_ == tInt)
> call_doit_er<int>();
> else if (t_ == tFloat)
> call_doit_er<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::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<BaseDoIt> er;
};

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

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

This is even safer. And cleaner! And faster!

Frank
  Réponse avec citation
Vieux 09/06/2008, 02h49   #3
joseph cook
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?

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


Template C'tor in a non template class? How would one possibly create
an object of this type (type B)?
  Réponse avec citation
Vieux 09/06/2008, 03h00   #4
Eric Pruneau
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?


<jason.cipriani@gmail.com> 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::printf("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_er is templated to access va_ as an A<T>.
> template <class T> void call_doit_er ();
> 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


  Réponse avec citation
Vieux 09/06/2008, 03h34   #5
Eric Pruneau
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?


"Frank Birbacher" <bloodymir.crap@gmx.net> a écrit dans le message de news:
6b2qudF397t59U1@mid.dfncis.de...
> Hi!
>
> jason.cipriani@gmail.com schrieb:
>> // 'A' is just a run-of-the-mill template class.
>> template <class T> class A {
>> public:
>> void doit () {
>> std::printf("A<%s>::doit()\n", typeid(T).name());
>> }
>> };

>
> and
>
>> void B::call_doit () {
>>
>> if (t_ == tInt)
>> call_doit_er<int>();
>> else if (t_ == tFloat)
>> call_doit_er<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::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<BaseDoIt> er;
> };
>
> template<typename T>
> B::B()
> : er(new A<T>())
> {
> }
>
> void B::call_doit()
> {
> er->doit();
> }
>
> This is even safer. And cleaner! And faster!
>
> Frank


er is a member function of B???

Can't call a member function in the constructor initializer list...


  Réponse avec citation
Vieux 09/06/2008, 03h43   #6
Gianni Mariani
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?

jason.cipriani@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.
  Réponse avec citation
Vieux 09/06/2008, 10h28   #7
James Kanze
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?

On Jun 9, 4:00 am, "Eric Pruneau" <eric.prun...@cgocable.ca> wrote:
> <jason.cipri...@gmail.com> a é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:james.kanze@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
  Réponse avec citation
Vieux 09/06/2008, 11h44   #8
Frank Birbacher
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?

Hi!

Eric Pruneau schrieb:
> er is a member function of B???
>
> Can't call a member function in the constructor initializer list...


"er" 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
  Réponse avec citation
Vieux 09/06/2008, 11h59   #9
Frank Birbacher
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?

joseph cook schrieb:
>> // non template class
>> struct B
>> {
>> // but template ctor:
>> template<typename T>
>> B();
>> void call_doit();
>> private:
>> const std::auto_ptr<BaseDoIt> er;
>>
>> };
>>
>> template<typename T>
>> B::B()
>> : er(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
: er(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<BaseDoIt> inst)
: er(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
  Réponse avec citation
Vieux 09/06/2008, 13h05   #10
Thomas J. Gritzan
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Is this void* cast safe?

Gianni Mariani schrieb:
> jason.cipriani@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
  Réponse avec citation
Réponse


Outils de la discussion

Règles de messages
Vous ne pouvez pas créer de nouvelles discussions
Vous ne pouvez pas envoyer des réponses
Vous ne pouvez pas envoyer des pièces jointes
Vous ne pouvez pas modifier vos messages

Les balises BB sont activées : oui
Les smileys sont activés : oui
La balise [IMG] est activée : oui
Le code HTML peut être employé : non
Trackbacks are oui
Pingbacks are oui
Refbacks are oui


Fuseau horaire GMT +1. Il est actuellement 03h46.


Édité par : vBulletin® version 3.7.3
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.2.0 RC5 Tous droits réservés.
Version française #16 par l'association vBulletin francophone
PHWinfo est un site Éducation Sans Frontières ©2000-2008
Ad Management by RedTyger
©Tous droits réservés par les parties respectives
Page generated in 0,23496 seconds with 18 queries