How can I extend a lexical cast to support enumerated types?
You have to do two steps. Finding an integral type large enough to store the values. You could use unsigned long
, but the values could be negative. Then you could use long
but the values could extend into the range of unsigned long
. So there is not really a fit-it-all type.
There is a trick though, by using overload resolution. Here is it
template<typename T>
struct id { typedef T type; };
id<char[1]>::type &find_etype(int);
id<char[2]>::type &find_etype(unsigned int);
id<char[3]>::type &find_etype(long);
id<char[4]>::type &find_etype(unsigned long);
You can change it appropriately to cover also long long
or unsigned long long
if your implementation has support for that. Now, passing an enum type will prefer one of these over all the other ones - that's a type that can store all values of it. You just need to pass sizeof
of the return type to some template.
template<int> struct get_etype;
template<> struct get_etype<1> { typedef int type; };
template<> struct get_etype<2> { typedef unsigned int type; };
template<> struct get_etype<3> { typedef long type; };
template<> struct get_etype<4> { typedef unsigned long type; };
Now, you can get a right type. All you need now is to see whether some type is an enumeration. How to do this is described in the book "C++ Templates - The complete Guide", and unfortunately is a whole lot of code. So i would use boost's is_enum
. Putting it together, it could look like
template <typename T>
typename boost::disable_if< boost::is_enum<T>, bool>::type
ConvertString(const std::string& theString, T& theResult)
{
std::istringstream iss(theString);
return !(iss >> theResult).fail();
}
template <typename T>
typename boost::enable_if< boost::is_enum<T>, bool>::type
ConvertString(const std::string& theString, T& theResult)
{
typedef typename get_etype<sizeof find_etype(theResult)>::type
safe_type;
std::istringstream iss(theString);
safe_type temp;
const bool isValid = !(iss >> temp).fail();
theResult = static_cast<T>(temp);
return isValid;
}
Hope this helps.
How can I extend a lexical cast to support enumerated types?
You have to do two steps. Finding an integral type large enough to store the values. You could use unsigned long
, but the values could be negative. Then you could use long
but the values could extend into the range of unsigned long
. So there is not really a fit-it-all type.
There is a trick though, by using overload resolution. Here is it
template<typename T>
struct id { typedef T type; };
id<char[1]>::type &find_etype(int);
id<char[2]>::type &find_etype(unsigned int);
id<char[3]>::type &find_etype(long);
id<char[4]>::type &find_etype(unsigned long);
You can change it appropriately to cover also long long
or unsigned long long
if your implementation has support for that. Now, passing an enum type will prefer one of these over all the other ones - that's a type that can store all values of it. You just need to pass sizeof
of the return type to some template.
template<int> struct get_etype;
template<> struct get_etype<1> { typedef int type; };
template<> struct get_etype<2> { typedef unsigned int type; };
template<> struct get_etype<3> { typedef long type; };
template<> struct get_etype<4> { typedef unsigned long type; };
Now, you can get a right type. All you need now is to see whether some type is an enumeration. How to do this is described in the book "C++ Templates - The complete Guide", and unfortunately is a whole lot of code. So i would use boost's is_enum
. Putting it together, it could look like
template <typename T>
typename boost::disable_if< boost::is_enum<T>, bool>::type
ConvertString(const std::string& theString, T& theResult)
{
std::istringstream iss(theString);
return !(iss >> theResult).fail();
}
template <typename T>
typename boost::enable_if< boost::is_enum<T>, bool>::type
ConvertString(const std::string& theString, T& theResult)
{
typedef typename get_etype<sizeof find_etype(theResult)>::type
safe_type;
std::istringstream iss(theString);
safe_type temp;
const bool isValid = !(iss >> temp).fail();
theResult = static_cast<T>(temp);
return isValid;
}
Hope this helps.
C++ convert string to enum?
You're not using the convert_string
function correctly.
typedef typename std::underlying_type::type safe_type;
This is determining the storage type for the enum's value (e.g., int, long long, etc).
Then this type is used to convert theString
to temp
which is a numeric type. Therefore, the istringstream
works by converting a string representing a numeric value (e.g., "0", "1", etc) to a numeric value.
Given this information you should understand why your code isn't working but the the following code does.
Code
enum TestType
{
A1,
B2,
C3
};
template<typename T>
typename std::enable_if<std::is_enum<T>::value, bool>::type
convert_string(const std::string& theString, T& theResult)
{
typedef typename std::underlying_type<T>::type safe_type;
std::istringstream iss(theString);
safe_type temp;
const bool isValid = !(iss >> temp).fail();
theResult = static_cast<T>(temp);
return isValid;
}
int main()
{
std::string s = "0"; // This is the value of A1
TestType tt;
std::cout << std::boolalpha << convert_string(s, tt) << "\n";
std::cout << std::boolalpha << (A1 == tt) << "\n";
return 0;
}
Output
true
true
Solution
To support your usage you'll need to do something like others have suggested in the comments (e.g., mapping of string representation to enum)
How can I partially specialize a class template for ALL enums?
use C++11 and SFINAE.
#include <type_traits>
template<typename T, typename = void>
struct Specialize
{
};
template<typename T>
struct Specialize<T, typename std::enable_if<std::is_enum<T>::value>::type>
{
void convert() { }
};
enum E
{
};
int main()
{
Specialize<E> spec;
spec.convert();
}
Without C++11 use boost::enable_if
and boost::is_enum
Enum? extends interface
One option you have is to add any of the methods from Enum you need onto Fooable or create a new interface that extends Fooable and adds the Enum methods you need.
Example:
interface Fooable {
void someCommonMethod();
}
interface FooableEnum extends Fooable {
int ordinal();
}
enum E1 implements FooableEnum {
// Implement someCommonMethod.
// ordinal() is already implemented by default.
}
Once you've done this you can use FooableEnum
as the parameter type in your method signature and not worry about any of the generic stuff.
Template specialization for enum
You can use std::enable_if
with std::is_enum
from <type_traits>
to accomplish this.
In an answer to one of my questions, litb posted a very detailed and well-written explanation of how this can be done with the Boost equivalents.
Lexical cast from string to type
I like using locate
, which works on built-in types:
>>> from pydoc import locate
>>> locate('int')
<type 'int'>
>>> t = locate('int')
>>> t('1')
1
...as well as anything it can find in the path:
>>> locate('datetime.date')
<type 'datetime.date'>
>>> d = locate('datetime.date')
>>> d(2015, 4, 23)
datetime.date(2015, 4, 23)
...including your custom types:
>>> locate('mypackage.model.base.BaseModel')
<class 'mypackage.model.base.BaseModel'>
>>> m = locate('mypackage.model.base.BaseModel')
>>> m()
<mypackage.model.base.BaseModel object at 0x1099f6c10>
function template for serialization of enums
There are two improvements to be made here: not always serializing as int
(not all enums are), but as whatever the underlying type is. And, as your request, to only accept enums.
The latter is easily solved with std::enable_if
and std::is_enum
:
typename std::enable_if<std::is_enum<T>::value, Stream&>::type
operator<<( Stream& s, T const& value )
// and likewise for operator>>
And for the former, do the following inside the function:
Stream& operator<<( Stream& s, T const& value )
{
typedef typename std::underlying_type<T>::type safe_type;
s << static_cast<safe_type>(value);
}
// and likewise for operator>>
This requires C++0x.
If that's not an option, both enable_if
and is_enum
can be found within Boost. However, I think you'll need to make underlying_type
yourself. (And of course, in the worse case you can do all three yourself, though is_enum
can be a pain, if I recall correctly.)
how to throw boost bad lexical cast exception
Remove the first throw
. boost::throw_exception
is a function that simply performs on its own. More specifically, boost::throw_exception
takes an exception as an argument, and uses a copy of that passed argument to throw an exception.
if(!Throw)
return boost::lexical_cast<std::string>(some_value);
else
boost::throw_exception(boost::bad_lexical_cast());
boost::throw_exception
is actually part of the Boost.Exception library, which uses C++ in some clever ways to allow you to attach additional information to exceptions while retaining the original exception types. Take a look at the linked documentation for more information.
C++ string to enum
A std::map<std::string, MyEnum>
(or unordered_map
) could do it easily. Populating the map would be just as tedious as the switch statement though.
Edit: Since C++11, populating is trivial:
static std::unordered_map<std::string,E> const table = { {"a",E::a}, {"b",E::b} };
auto it = table.find(str);
if (it != table.end()) {
return it->second;
} else { error() }
Related Topics
How to Get File Extension from String in C++
C++ Best Way to Get Integer Division and Remainder
Sharing Precompiled Headers Between Projects in Visual Studio
How to Pass Derived Classes by Reference to a Function Taking Base Class as a Parameter
Why Does the C++ Standard Algorithm "Count" Return a Difference_Type Instead of Size_T
Why Must I Put a Semicolon at the End of Class Declaration in C++
C++ Floating Point to Integer Type Conversions
Parameter Name Omitted, C++ VS C
Can Lambda Functions Be Recursive
Error::Make_Unique Is Not a Member of 'Std'
Broken C++ Std Libraries on MACos High Sierra 10.13
Error: Class Has Not Been Declared Despite Header Inclusion, and the Code Compiling Fine Elsewhere
Memory Allocation Profiling in C++