473,386 Members | 1,674 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

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 2028
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 thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: johny smith | last post by:
I have never really understood the purpose of the void*. I guess it is so different data types could for instance be passed into a function. And then the function will re cast it to the correct...
188
by: infobahn | last post by:
printf("%p\n", (void *)0); /* UB, or not? Please explain your answer. */
34
by: Alawna | last post by:
Hi, i was reading a chapter about c pointers in "c++builder complete reference" and it is said there that malloc returns a pointer to the start of the memory allocated but i see the prototype of...
6
by: rouble | last post by:
Hi All, Is it safe to store a uchar in a void* and then extract the uchar value out of it again ? My understanding is that the size of a void* should always be equal to or greater than the...
27
by: Erik de Castro Lopo | last post by:
Hi all, The GNU C compiler allows a void pointer to be incremented and the behaviour is equivalent to incrementing a char pointer. Is this legal C99 or is this a GNU C extention? Thanks in...
9
by: Alex Vinokur | last post by:
Is this approach safe? class Foo { // Stuff }; void func1 (void *) { // Do something
5
by: WittyGuy | last post by:
How to typecast a "function pointer" to "const void*" type in C++ way? int MyFunction (double money); // Function prototype const void* arg = (const void*)MyFunction; // type casting...
35
by: =?utf-8?b?QXNiasO4cm4gU8OmYsO4?= | last post by:
This topic is a FAQ. But I have read the faq and spent a couple of hours browsing the group archives, and still have a few questions that I hope you can answer. My understanding is that...
7
by: Charles Zhang | last post by:
I want to know to the following in a safe manner (the following code will be dangerous if p does not contain a pointer to MyClass). I would like someone to show me a better way (either throw an...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.