Re: Seeking code for temple for a <map> of enum to string
On Mar 29, 10:18 pm, "AnonMail2...@gmail.com" <AnonMail2...@gmail.com>
wrote:
On Mar 15, 11:04 pm, Baron Samedi <Papa.Legba....@gmail.com> wrote:
I know that it's an old one - how to take an enum value and
output astring.
I'd like to make it a template class, so that I can add to it in a
general manner.
This is a common task. Of course, inside you program,
library, or collection of libraries you want to just use your
enumerator. But what you've defined as an enumerator may come
in from the external world as text.
Textual representation also comes in handy for error messages
and when serializing data. Serializing an enumerator as an int
is a future maintenance nightmare just waiting to happen.
Doing so means you can't change the value of an emuerator
without worrying about breaking something.
Well, the obvious answer to that is "don't change them":-).
Seriously, you do need some sort of versioning, even if you're
using text represtations for serialization, since you have to
deal with enum values being added and removed. (Of course, all
of the other arguments for textual representation still hold.
Reading "operationPending" for the state, rather than "5", makes
debugging considerably easier.)
What we have done is implement three template functions for each
enumerator.
Here are their signatures:
// convert a string to an enumerator - throw an exception if the
string is invalid
template <class T>
T stringToEnum (const std::string & s)
// convert a string to an enumerator
// on success, return true and set t; on failure, return false and t
is not touched
template <class T>
bool stringToEnum (const std::string & s, T & t)
// convert an enumerator to string (actually const char *)
template <class T>
const char * enumToSTring (T t);
Just a question: you have generic templates to do this? I can't
see how (but maybe I've missed something).
In my case, I use a (originally) simple program, capable of
understanding enough C++ to parse the enums, to generate the
implementations of these. Also, I've found that the template
solution poses some problems, mainly with regards to namespaces;
my solution has been to generate functions toString( EnumType )
and to<EnumType>( string ). And a third, fromString( string,
EnumType& ) for use in templates. (Having the EnumType in an
argument makes the function name dependent, and causes ADL to
kick in.) These functions are generated in the same namespace
as the enum.
I originally used the template approach, but couldn't find an
appropriate namespace to put it in. (More exactly, I orginally
implemented the code as part of my library, with the templates
in the same namespace as the library code. This version is
available at my site: http://kanze.james.neuf.fr/code-en.fr. I
have since changed the implementation to remove all dependencies
on my library in the generated code. Having done this, I didn't
want it to use the namespace Gabi, and I couldn't find another
appropriate namespace for the templates. I actually liked the
idea of naming the function templates string_cast, and putting
them in global namespace, but I was too worried about possible
conflicts with other code.)
FWIW, the current implementation has options to generate either:
Gabi::Util::Fallible< std::string > toString( enum_type ) ;
Gabi::Util::Fallible< enum_type > toenum_type( std::string
const& ) ;
bool fromString( enum_type&, std::string const& ) ;
(for myself and others using my library) or
char const* toString( enum_type ) ;
enum_type const* toenum_type( std::string const& ) ;
bool fromString( enum_type&, std::string const& ) ;
(With enum_type being the type of the enum, of course). When
all is said and done, however, I think that in this case, using
Fallible instead of pointers may be overkill.
Internally these are implemented using a static array of pairs
of const char * and enumerators. Using a static array of POD
means there are no of order of initialization or thread safety
issues that you might encounter using a std::map (for
instance) or other C++ class.
Agreed. At one point, I toyed with the idea of having two
std::map at the interface level, so that users would have a well
known interface to deal with, rather than something I define
myself. In the end, both order of initialization and naming
problems lead me to abandon this approach.
Lookup is done using linear search. We've never had any
performance issues with this. When converting from string to
enumerator we use case insensitive compares.
That's an interesting idea. Sounds like I'll have to add an
option to my generator.
Different strings can be mapped to the same enumerator value
if needed.
And different enumerators to the same string?
When converting from enumerator to string the first entry in
the array will be the "preferred" text representation.
I do a one to one mapping, but at the symbolic level (name
to/from string); if two enum constants have the same value, then
I handle it as you do---first match is used.
I also permit (in the current versions) transformations in the
name, mainly because I had code available which supported this
already, and I've encountered places which had special naming
conventions for enum constants, that you possibly wouldn't want
to appear in the external representations.
What's more, we've hidden all of the implementation by
explicitly instantiating each function inside the
implementation file. The user of the functions never see
anything but the plain old enumerator and the signature of the
above classes.
So you do have an explicit specialization for each enum. Hand
written, or automatically generated?
Note that for the code to be conform, you do need for a
declaration of the specification to be visible anytime it is
used.
These functions go a long way to solving the enumerator to
string and string to enumerator task. With them, writing an
operator<< or operator>> is trivial.
The code is done at work - I will ask if it is ok to post it.
But the code is probably simpler than defining the interface:-).
A simple struct, a couple of predicates, and std::find_if should
do the trick.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34