|
|
|
#1 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
> // 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)? |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
<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 |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
"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... |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
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 |
|
![]() |
| Outils de la discussion | |
|
|