Sign in to follow this  
Koobazaur

Storing a type of a variable?

Recommended Posts

The type is implicit in case of templates:

template < class T >
struct Holder
{
T value;
};

Holder<int> int_var;
std::cout << typeid(int_var.value).name() << std::endl;


This should give you the int type, since compiler creates one class for each unique Holder instance, and that one is strongly typed.

Share this post


Link to post
Share on other sites
Basically I am creating a class that can take an any value, int, float, string etc. I was pondering using templates, but decided on a void pointer since it's more flexible (I can change the value of my class at any point). Setting the value is easy, but retrieving must know exactly what type it is or bad things happen. Hence why I wanted the entry to know it's own type so that it can be always checked before retrieving the value.

Share this post


Link to post
Share on other sites
Aye, I know about boost::any, but I wanted to create this sort of thing myself. funnyily enough, my interface looks exactly like the one from boost::any, with templates for the = operators.

basically what I do is store a void pointer and a string that defines my typeid.name(). Everything works fine, except that I am not sure how to create the copy constructor for my class. I would need to create a copy of the pVoid, but to do that I would need to call new with the type, but is there a way to do that with just the name of the type from typeid.name() ?

Share this post


Link to post
Share on other sites
Hm.

Well for POD it's simple, just do a memcpy of the data.

For non POD types, you have to change your implementation. You'd need a template holder class that holds the actual object that inherits from a all purpose parent holder class. Your implementation then stores a parent holder pointer instead of a void pointer. This polymorphic pointer allows you to forward the copy construction to an object that knows the held type. The holder class needs to implement a method that duplicates the object contained using new.

It's not beginner stuff, but it can be done.

Share this post


Link to post
Share on other sites
Quote:
Original post by King Mir
Well for POD it's simple, just do a memcpy of the data.

To do a memcpy you need to somehow translate the stored type-name into a size_t...

Quote:
Original post by Koobazaur
basically what I do is store a void pointer and a string that defines my typeid.name(). Everything works fine, except that I am not sure how to create the copy constructor for my class. I would need to create a copy of the pVoid, but to do that I would need to call new with the type, but is there a way to do that with just the name of the type from typeid.name() ?

Instead of using a string, I'd use an enumeration (but that's just me). Then in your copy function, you'll have to do a switch on that enum value (or a big if/else block on your string value).

enum TypeName
{
Int, Float /*etc*/
};

class CClass
{
public:
CClass( const CClass& o ) : m_Type(o.m_Type)
{
switch( m_Type )
{
case Int: m_Value = new int; *((int*) m_Value) = o.m_Value?*((int*) o.m_Value):0; break;
case Float: m_Value = new float; *((float*)m_Value) = o.m_Value?*((float*)o.m_Value):0; break;
default: m_Value = 0;
}
}
private:
TypeName m_Type;
void* m_Value;
}


[Edited by - Hodgman on January 3, 2008 12:02:49 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
Quote:
Original post by Koobazaur
basically what I do is store a void pointer and a string that defines my typeid.name(). Everything works fine, except that I am not sure how to create the copy constructor for my class. I would need to create a copy of the pVoid, but to do that I would need to call new with the type, but is there a way to do that with just the name of the type from typeid.name() ?

Instead of using a string, I'd use an enumeration (but that's just me). Then in your copy function, you'll have to do a switch on that enum value (or a big if/else block on your string value).


*** Source Snippet Removed ***

That would work, except that it would require the enumeration to store every conceivable type. That's just not practical. Even limiting ourselves to non class types, you'd have to handle multiple levels of indirection, with possible variations in constness and volatile-ness(volatility?). Consider the type:

const int * volatile *const volitile *

Share this post


Link to post
Share on other sites
It depends on Koobazaur's requirements.
These kinds of loosely typed variables are generally used for scripting and the like, where keywords such as const and volatile (and to a certain degree, even pointers) are probably not often required.

With the string-based approach, wouldn't you also need to write a if/elseif/else block containing every possible type?
e.g.
if( m_strType == typeid(int).name() ) m_value = new int;
else if( m_strType == typeid(float).name() ) m_value = new float;
...

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
With the string-based approach, wouldn't you also need to write a if/elseif/else block containing every possible type?
e.g.
if( m_strType == typeid(int).name() ) m_value = new int;
else if( m_strType == typeid(float).name() ) m_value = new float;
...
You could just do this:

template <typename T> T any::get(){
if( m_typename == typeid(T).name() )
return (T) *m_vptr;
return 0;//or throw an exception.
}


But that doesn't work for copy constructors. For that you'd need what I described earlier.

Share this post


Link to post
Share on other sites
Umm... you've lost me, sorry.
Recap: Current implementation uses a type-string and void*, I suggest type-enum so a switch can be used (for cloning) instead of nested-if, you point out that the enum/switch would be huge, I propose that it would be no different in size to the current (string-based) method's required nested-if... To which you propose as a solution a templated get-value function, which is unrelated to run-time cloning?

Back on track, I agree about the polymorphic holder class as a much more general solution...

Share this post


Link to post
Share on other sites
I just realized that a similar problem will arise with the destructor as does with the copy constructor. The polymorphic solution is unchanged. However, If you keep the void pointer then you can't use normal delete, because delete cannot take a void pointer. So you must use either malloc()/calloc() and free() or preferably ::operator new(size_t) and ::operator delete(void *).

Missed this earlier:
Quote:
Original post by Hodgman
Quote:
Original post by King Mir
Well for POD it's simple, just do a memcpy of the data.

To do a memcpy you need to somehow translate the stored type-name into a size_t...
You can store the size as an extra member variable.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this