Enum to String and vice versa in C++
2009-09-04 13:26
633 查看
在国外一网站上看到一篇字符串和枚举类型之间的转换,贴出来给大家看下:
文章的源代码可以通过如下链接下载:源代码
However, all the solutions I found suffered from one or more of the following:
No support for enumerators with non-contiguous values
No support (not even partial support) for enumerators with duplicate values
No support for existing enumerations (without modifying their source code)
Requires one or more extra files per enumeration
Requires source-code to be pre-processed (by a custom binary) before compilation
Difficult to use or maintain
Highly susceptible to typos
Is platform/compiler specific (not portable)
So (the rip-off that I am), I borrowed the good ideas from all the solutions I found, added a few of my own and mixed-and-matched to create the code which accompanies this article. I don't claim this to be the best solution for every case, just that it solved my problem nicely and that it could be of use to someone else as well.
Let's say we wanted to create an enum to represent one of the Furious Five Masters (Kung Fu Panda (2008) anyone?). So we go ahead and declare it as usual:
Don't worry about the values assigned, they are simply my rough idea of what the 'mass' of each master is, in some imaginary units. Now to add stringizing support, we need to declare the enum again, but in a different format (uses helper macros):
And we're done! Note that since this second declaration lies in the same header/source file as the actual enum definition (probably declared just below it), it's not that difficult to update it whenever we modify the actual enumeration.
Now we can convert from a string to a Master enumerator and vice versa, quite easily. The following code shows how to do that:
Say that we can't modify the library files (which is anyway not a good practice). So we create a separate header file in our project, in which we will declare stringizing support for the required library enum. For the declaration we have 3 options:
The consequence of this is that the stringized enums will also be fully qualified names. So, the statement EnumString<WeekEnd>::From( SomeLibrary::Saturday ) will yield "SomeLibrary::Saturday", and not just "Saturday".
If you look at the declarations of the helper macros, you'll see that the definition of the string support for FuriousFiveMaster, works out to the following:
You might have already realized that the above is a specialization of the EnumString template class. It defines the RegisterEnumerators function which is used by its base class EnumStringBase, via CRTP. Now the workings of the usage becomes clear. When you use the functions EnumString<Master>::From or EnumString<Master>::To, you're using the version of the EnumString template class which is specialized with the Master enumeration.
Doesn't support conversion of enumerators with duplicate values to strings (although vice versa works just fine). An attempt to convert such an enumerator, will yield an empty string (which can be tested for).
Conversion performance might be a bottleneck for some applications. A single std::map is used internally for storing the relationship of enumerators to their string representations. So lookups during conversions are not in constant time. A conversion from an enumerator to a string will be in linear time, although a vice versa conversion should be very fast.
Microsoft Visual C++ 2005/2008
GCC 4.4.0
There is much potential for improvement of the code. But since the current code is good enough for my needs, I'll leave that for someone else to do. If you make changes to the code, improve it, or simply have an entirely different and better way to solve this, please share it with me also; I might just dump my code and use yours instead! :)
文章的源代码可以通过如下链接下载:源代码
Introduction
While adding Serialization support to my project, I realized that I would require some way to convert a string to an enumerator and vice versa. So I did a Google search for the same, and found a lot of information; different ways in which people implemented this functionality.However, all the solutions I found suffered from one or more of the following:
No support for enumerators with non-contiguous values
No support (not even partial support) for enumerators with duplicate values
No support for existing enumerations (without modifying their source code)
Requires one or more extra files per enumeration
Requires source-code to be pre-processed (by a custom binary) before compilation
Difficult to use or maintain
Highly susceptible to typos
Is platform/compiler specific (not portable)
So (the rip-off that I am), I borrowed the good ideas from all the solutions I found, added a few of my own and mixed-and-matched to create the code which accompanies this article. I don't claim this to be the best solution for every case, just that it solved my problem nicely and that it could be of use to someone else as well.
How to use the code
Using the code is quite easy. All we need do is add one file: EnumString.h (see the source code accompanying this article), to our project.// Furious Five Master enum Master { Tigress = 5, Viper = 3, Monkey = 4, Mantis = 1, Crane = 2 };
Let's say we wanted to create an enum to represent one of the Furious Five Masters (Kung Fu Panda (2008) anyone?). So we go ahead and declare it as usual:
Don't worry about the values assigned, they are simply my rough idea of what the 'mass' of each master is, in some imaginary units. Now to add stringizing support, we need to declare the enum again, but in a different format (uses helper macros):
// String support for Furious Five Master Begin_Enum_String( Master ) { Enum_String( Tigress ); Enum_String( Viper ); Enum_String( Monkey ); Enum_String( Mantis ); Enum_String( Crane ); } End_Enum_String;
And we're done! Note that since this second declaration lies in the same header/source file as the actual enum definition (probably declared just below it), it's not that difficult to update it whenever we modify the actual enumeration.
Now we can convert from a string to a Master enumerator and vice versa, quite easily. The following code shows how to do that:
// Convert from a Master enumerator to a string const std::string &masterStr = EnumString<Master>::From( Monkey ); assert( masterStr.compare( "Monkey" ) == 0 ); // Convert from a string to a Master enumerator Master master = Tigress; const bool bResult = EnumString<Master>::To( master, masterStr ); assert( bResult == true ); assert( master == Monkey );
Using the code with existing enumerations
Suppose we want to add stringizing support to an existing enumeration from a library, which is namespaced. Imagine that the enum is declared like this in the libary:namespace SomeLibrary { enum WeekEnd { Sunday = 1, Saturday = 7 }; }
Say that we can't modify the library files (which is anyway not a good practice). So we create a separate header file in our project, in which we will declare stringizing support for the required library enum. For the declaration we have 3 options:
Option 1 - Fully qualify all the names:
Begin_Enum_String( SomeLibrary::WeekEnd ) { Enum_String( SomeLibrary::Sunday ); Enum_String( SomeLibrary::Saturday ); } End_Enum_String;
The consequence of this is that the stringized enums will also be fully qualified names. So, the statement EnumString<WeekEnd>::From( SomeLibrary::Saturday ) will yield "SomeLibrary::Saturday", and not just "Saturday".
Option 2 - Use the 'using namespace' directive:
using namespace SomeLibrary; Begin_Enum_String( WeekEnd ) { Enum_String( Sunday ); Enum_String( Saturday ); } End_Enum_String;
Option 3 - Register the enumerators yourself
Without using the 'Enum_String' helper macro (see the next section 'How does it actually work?', for an explanation of this):Begin_Enum_String( SomeLibrary::WeekEnd ) { RegisterEnumerator( SomeLibrary::Sunday, "Sunday" ); RegisterEnumerator( SomeLibrary::Saturday, "Saturday" ); } End_Enum_String;
How does it actually work?
It's not required to know how it works in order to use it, so those who are not really interested can skip this section. Also, beginners might have to brush up on their C++ before reading this.If you look at the declarations of the helper macros, you'll see that the definition of the string support for FuriousFiveMaster, works out to the following:
template <> struct EnumString<Master> : public EnumStringBase< EnumString<Master>, Master > { static void RegisterEnumerators() { RegisterEnumerator( Tigress, "Tigress" ); RegisterEnumerator( Viper, "Viper" ); RegisterEnumerator( Monkey, "Monkey" ); RegisterEnumerator( Mantis, "Mantis" ); RegisterEnumerator( Crane, "Crane" ); } }
You might have already realized that the above is a specialization of the EnumString template class. It defines the RegisterEnumerators function which is used by its base class EnumStringBase, via CRTP. Now the workings of the usage becomes clear. When you use the functions EnumString<Master>::From or EnumString<Master>::To, you're using the version of the EnumString template class which is specialized with the Master enumeration.
Drawbacks
As with most things, the code does have some drawbacks. The two most important ones are:Doesn't support conversion of enumerators with duplicate values to strings (although vice versa works just fine). An attempt to convert such an enumerator, will yield an empty string (which can be tested for).
Conversion performance might be a bottleneck for some applications. A single std::map is used internally for storing the relationship of enumerators to their string representations. So lookups during conversions are not in constant time. A conversion from an enumerator to a string will be in linear time, although a vice versa conversion should be very fast.
Closing
Since the code makes extensive use of templates, it may not work with older compilers. The code has been tested with the following compilers:Microsoft Visual C++ 2005/2008
GCC 4.4.0
There is much potential for improvement of the code. But since the current code is good enough for my needs, I'll leave that for someone else to do. If you make changes to the code, improve it, or simply have an entirely different and better way to solve this, please share it with me also; I might just dump my code and use yours instead! :)
相关文章推荐
- wstring to string and vice versa
- Convert a string to a byte array and vica-versa in C#
- Convert a String In C++ To Upper or Lower Case
- Android NDK编译C++ 'stoi' and 'to_string' is not a member of 'std'
- How to Copy Archivelog Files From ASM to Filesystem and vice versa
- Steps To Migrate-Move a Database From Non-ASM to ASM And Vice-Versa
- how to remove nil and blank string in an array in Ruby
- How to do run-time (or explicit) linking of C++ plug-in components and objects
- Convert IP addresses from a dots-and-number string to a struct in_addr and back
- Read and Write to a Keyboard device in Linux using C++
- How to call c++ exported method and classes in c#
- How can I convert a QString to char* and vice versa?
- Steps To Migrate-Move a Database From Non-ASM to ASM And Vice-Versa
- C# : Enum and overriding ToString on it
- string to number code in C++ template
- Easiest way to convert int to string in C++
- effective C++ 2 prefer consts, enums and inlines to #defines
- How can I convert NSDictionary to NSData and vice versa?
- google-gson - A Java library to convert JSON to Java objects and vice-versa
- How to use libcurl in C++ with std::string