jrbcast said:
Thank you very much, I did not realize that "proxy" was the key word
to google it :-S. Sorry, too late here after work (Spain = 21:54).
I can not imagine how this proxy hides types management in a
heterogeneous structure like that I am proposing here std::map<
std::string, void *> so the caller does not need to explicitly pass
the type it is expecting, just receive it (the called object will care
about that)...
I have written something like this but I would like (if possible) to
know if there is a more "advanced" or "correct" way to do that:
// PLEASE NOTE CODE HAS NOT BEEN COMPILED YET AND MAY BE ERRONEOUS
#define angle_t double
#define rot(object) *((angle_t*)(object.getValue(std::string("rot"))))
#define tilt(object) *((angle_t*)
(object.getValue(std::string("tilt"))))
#define psi(object) *((angle_t*)(object.getValue(std::string("psi"))))
class objectValues
{
std::map<std::string, void *> values;
objectValues();
~objectValues();
int addValue( std::string name, int value )
{
void * newValue = (void *)(new int(value));
return insertVoidPtr( name, newValue );
}
int addValue( std::string name, double value )
{
void * newValue = (void *)(new double(value));
return insertVoidPtr( name, newValue );
}
int insertVoidPtr( std::string name, void * value )
{
// Return value for "insert" call
pair<map<std::string, void *>::iterator,bool> ret;
ret = values.insert( pair<std::string, void*>(name,value) );
if (ret.second==false)
{
return -1;
}
else
{
return 0;
}
}
void * getValue( std::string name )
{
map <std::string, *void>::const_iterator element;
element = values.find( name );
if ( element == values.end( ) )
{
return NULL;
}
else
{
return element->second;
}
}
bool valueExists( std::string name )
{
map <std::string, *void>::const_iterator element;
element = values.find( name );
if ( element == values.end( ) )
{
return false;
}
else
{
return true;
}
}
void deleteValue( std::string name )
{
map <std::string, *void>::const_iterator element;
element = values.find( name );
if ( element != values.end( ) )
{
values.erase( element );
}
}
}
You may be in the same situation as I have at a couple of places, where
I need to store values of any type in a container to be able to process
them later.
Following is a simplification of my own solution, but please note that I
have not found this solution anywhere on the net or in any book, so I
don't know if this is the right way to go. However, it seems to work
pretty good so far. The solution is built on the Visitor pattern.
http://en.wikipedia.org/wiki/Visitor_pattern
In this solution you first have to identify the receiver of your object
values. I.e. it may not be the perfect solution if you want to create a
generic map that can deliver your objects to whatever. In my case I
wanted to deliver a container of any type to std::cout. Basically I
wanted to create a container with contents like this
{ "Hello ", myOwnObject, " number ", 7, std::endl } and I couldn't
calculate it during the call, hence, I first needed to store the values
in a container until they should be used.
Ok, back to step one. I identified the receiver as std::cout. Then I
created an objectValue-to-receiver interface like this.
class ObjectValueInterface
{
public:
virtual ~ObjectValueInterface() {}
virtual void applyTo( std:
stream& ) = 0;
// applyTo() would represent accept() in the Visitor pattern.
};
// Then create a template for any object value like this.
// Here you also need to decide whether you want a copy of your object
// or a (smart) pointer to your object. Since you copy in your own
// example I do the copy variant here.
template<typename T>
class ObjectValue : public ObjectValueInterface
{
public:
ObjectValue( T objVal ) : objVal_(objVal) {}
virtual void applyTo( std:
stream& os )
{
os << objVal_;
// operator<<() would represent visit() in the Visitor pattern.
}
private:
T objVal_;
};
// Now you create your object value container.
#include <map>
class ObjectValueContainer
{
public:
template<typename T>
int addValue( const std::string& key, T val )
{
if( objVals_.find( key ) != objVals_.end() )
return -1;
objVals_[key] = new ObjectValue<T>( val );
return 0;
}
ObjectValueInterface* getValue( const std::string& key )
{
if( objVals_.find( key ) == objVals_.end() )
return NULL;
return objVals_[key];
}
private:
std::map<std::string, ObjectValueInterface*> objVals_;
// Note! This is a simplified example. You should consider using
// boost::shared_ptr instead of a plain pointer here.
// boost::shared_ptr will be std::shared_ptr with the next
// standard. Until then, see
//
www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_ptr.htm
};
// Now use it.
// Note! In this example we skip error handling.
int main()
{
ObjectValueContainer objVals;
// Add some values.
objVals.addValue( "Name", "Daniel" );
objVals.addValue( "House Number", 3 );
objVals.addValue( "Double", 47.11 );
objVals.addValue( "Delimiter", " " );
// Print the values.
objVals.getValue( "Name" )->applyTo( std::cout );
objVals.getValue( "Delimiter" )->applyTo( std::cout );
objVals.getValue( "House Number" )->applyTo( std::cout );
objVals.getValue( "Delimiter" )->applyTo( std::cout );
objVals.getValue( "Double" )->applyTo( std::cout );
return 0;
}
Hope this helps!
If anyone else have comments on this kind of solution I would be happy
to hear it so I can find flaws and improve it.
/Daniel