Re: How to elegantly get the enum code from its string type
On Apr 13, 11:53 am, thomas <freshtho...@gmail.com> wrote:
Hi guys,
I got a problem in practice, and I cannot find a verly elegan=
t
solution to it.
------------------------------------------------------------------------
enum Type{
REPLY = 100,
REP = 1052,
HAHA = 9523,};
------------------------------------------------------------------------
When I open the interface to users for configuration, I would like the
user to use "REPLY", "REP", "HAHA" like string format because it's
much easier to recognize and remember.
But in my program, I need to use the actual code (integral format).
My solution is to define a map<string, int>, and insert the string and
integral format pairs into the map for query. But it's really anoying
and difficult to use, especially when initializing.
Is there any elegant solution to this problem?
Thanks in advance.
Tom
boost::lexical cast uses operator >> to do conversions. By
overloading operators << and >> for a type T lexical
cast can easily be used:
This is more or less what I did in the code here below. I had to
subclass and provide the map (makeValueMap) as mentioned
by another poster. Hope it makes sense. Werner
class T_AsEnumBase
{
protected:
struct ECantConvert{};
};
// Provides the ability to be lexically casted (by operator >>).
// It is typically used to convert a string value representing type T
in a
// configuration file to its representing enumerated type.
// It is automatically convertible to the associated enumerated type.
// Notes:
// 1) Inherits from T_AsEnumBase - merely to share the exception type.
// 2) Classes deriving from this should override method "doConvert".
This method
// may only throw exception ECantConvert. Its purpose is to convert
the value
// to the representing enumerated type.
// 4.1) StringAsEnum<EnumT> : Derive from to convert string values to
EnumT.
// 4.2) IntAsEnum<EnumT> : Derive from to convert integral values to
EnumT.
// 5) See below for a typical example of derived implementation /
Usage.
// 6) If one does not overload operator << for the derived type (in
the same
// namespace!!!), std::operator << (std::ostream&, int ) will be used
(due to
// conversion operator operator EnumT()).
template <class T, class EnumT>
class T_AsEnum : public T_AsEnumBase
{
public:
T_AsEnum(): value_(){ }
EnumT get() const{ return value_; }
operator EnumT() const{ return get(); }
bool set( const T& value )
{
try
{
value_ = doConvert( value );
return true;
}
catch( ECantConvert e )
{
return false;
}
}
private:
//Note:
//- Throw ECantConvert int the event of a conversion failure.
//- All other exceptions are uncaught/unexpected.
virtual EnumT doConvert( const T& inVal ) const /
*throw(ECantConvert)*/ = 0;
EnumT value_;
};
template <class T, class EnumT>
std::istream& operator >> ( std::istream& input, T_AsEnum<T,EnumT>&
result )
{
T inVal;
if( input >> inVal )
{
if( result.set( inVal ) == false )
{
input.setstate( std::ios::failbit );
}
}
return input;
}
//Convenience classes to derive from.
template <class EnumT>
class IntAsEnum : public T_AsEnum<int, EnumT>{ };
template <class EnumT>
class StringAsEnum : public T_AsEnum<std::string, EnumT>{ };
// Description:
// This class uses an STL compliant map to convert from a string value
// to the corresponding enumerated value. Be default std::map is used.
// Note:
// 1) It is necessary to provide an implementation for function
makeValueMap.
// For an STL map (default) the implementation declaration will look
as follows:
// const std::map<std::string,EnumT>& makeValueMap() const;
template <class EnumT, class MapT = std::map<std::string, EnumT> >
class StringAsMappedEnum : public StringAsEnum<EnumT>
{
private:
virtual EnumT doConvert( const std::string& inVal ) const
{
static const MapT& valueMap = makeValueMap();
typename MapT::const_iterator pos = valueMap.find( inVal );
if( pos != valueMap.end() )
{
return pos->second;
}
throw T_AsEnumBase::ECantConvert();
}
virtual const MapT& makeValueMap() const = 0;
};