C++11 type to enum mapping? - c++

I have an enum like:
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
}
And I want to create a compile-time mapping to get the appropriate E for a type like:
GetE<float> // returns TYPE_FLOAT
GetE<char> // returns TYPE_CHAR
GetE<int> // returns TYPE_INT
I thought of:
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
But I'm getting errors like:
undefined reference to `GetE<int>::type'
Whats the best way to do this? And why the error?

It depends on how you use these constant expressions.
The ODR (one-definition rule) states that
(§3.2/2) [...] A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. [...]
(And then, lots of special rules, exceptions and exceptions of the exceptions follow.)
Any variable that is odr-used, must have exactly one definition. Your constant expressions have a declaration, but not a definition, so this goes well unless you odr-use one of them.
For example, the following goes well:
int main() {
E e = GetE<float>::type;
return 0;
}
But this does not:
void f(const E &)
{ }
int main() {
f(GetE<float>::type);
return 0;
}
because f requires a (const) reference, so the lvalue-to-rvalue conversion cannot be applied immediately, hence this constitutes an odr-use. The compiler will complain that it misses a definition.
(Remark. As ShafikYaghmour found (see the comments), you may not get a complaint if the compiler uses optimization, as the references may be optimized away. To reproduce the compiler complaint, use the -O0 flag (or similar, depending on the compiler).)
To solve the problem, the required definition can be provided in the usual way, i.e. outside the struct-definition:
constexpr E GetE<float>::type;
constexpr E GetE<char>::type;
constexpr E GetE<int>::type;
But since this would have to happen in the .cpp (not the header file), you'll end up having to maintain the declarations and definitions in two different places, which is cumbersome.
The solution you've just suggested in your comment, i.e. define a constexpr (and inline) function, sounds right:
template <class T> constexpr E GetE();
template <> constexpr E GetE<float>()
{ return TYPE_FLOAT; }
template <> constexpr E GetE<char>()
{ return TYPE_CHAR; }
template <> constexpr E GetE<int>()
{ return TYPE_INT; }
void f(const E &)
{ }
int main() {
E e = GetE<float>();
f(GetE<float>());
return 0;
}

Static member variables need to be defined outside the class scope:
class C {
const static int x = 5;
};
decltype(C::x) C::x;

Maybe because you forgot to put a semicolon after the enum definition, this works for me in LiveWorkSpace:
#include <iostream>
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
} ;
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
int main()
{
std::cout << GetE<int>::type << std::endl ;
}
here is a link to the code http://liveworkspace.org/code/nHqUe$6

Related

Creating a constexpr enum-like type

I've been using enum class FooEnabled : bool { no, yes }; as a way to create type-safe bools. It works well, except I'd like to add explicit conversion to bool, Boolean operators like operator!, etc. I can do that like this:
template <typename Tag>
class TypedBool {
bool value;
explicit constexpr TypedBool(bool b) noexcept : value(b) {}
public:
static inline TypedBool no{false};
static inline TypedBool yes{true};
explicit constexpr operator bool() const noexcept { return value; }
constexpr TypedBool operator!() const noexcept { return TypedBool{!value}; }
// ...
};
using FooEnabled = TypedBool<struct FooEnabledTag>;
That works great, however no and yes aren't constexpr, so I can't do if constexpr (FooEnabled::yes) { for example. If I make no and yes be instead static constexpr, clang is upset because TypedBool is not a literal type. That appears to be because TypedBool is incomplete at that point.
The simplest example of this is struct S { static constexpr S s; }; which gives
error: constexpr variable cannot have non-literal type 'const S'
struct S { static constexpr S s; };
^
note: incomplete type 'const S' is not a literal type
note: definition of 'S' is not complete until the closing '}'
struct S { static constexpr S s; };
Is there any way around this? I could make no and yes be a different type that implicitly converts to TypedBool<Tag>, but that seems weird, because then auto x = FooEnabled::yes; would make x not be a FooEnabled, so
auto x = FooEnabled::yes;
[](FooEnabled& x) { x = !x; }(x);
would fail.
Is there any way to have a class contain static constexpr members that are its own type? The solutions I'm starting to think of all seem too ugly to mention (and those also have constexpr limitations).
Is there any way to have a class contain static constexpr members that are its own type?
Yes, there is, just split the declaration from the definition, only the definition needs to contain constexpr.
struct Foo {
constexpr Foo(bool b): value(b){}
static const Foo yes;
static const Foo no;
constexpr explicit operator bool() const noexcept{return value;}
bool value;
};
// Mark inline if in a header.
inline constexpr const Foo Foo::yes{true};
inline constexpr const Foo Foo::no{false};
int main(){
if constexpr(Foo::yes){
return 5;
};
}
Isn't this different declaration vs definition ?
All three compilers g++,clang++,MSCV 19 accept the code above.
But if Foo is a template, clang++ doesn't compile the code anymore, as discovered in comments.
There is a question about this hinting the standard does not forbid this.
Unfortunately, C++17, C++20 standards are no more explicit either, stating:
The final C++17 draft requires of [dcl.constexpr][Empahis mine]
The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template.
The consteval specifier shall be applied only to the declaration of a function or function template.
A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable ([dcl.inline]).
If any declaration of a function or function template has a constexpr or consteval specifier, then all its declarations shall contain the same specifier.
So my take from this is this is allowed but maybe due to omission rather than deliberation. I did not manage to find any examples in the Standard that would validate this approach.
This is the closest syntax I know works
class TypedBool
{
public:
explicit constexpr TypedBool(bool value) noexcept :
m_value{ value }
{
}
static constexpr TypedBool no()
{
constexpr TypedBool value{ false };
return value;
}
static constexpr TypedBool yes()
{
constexpr TypedBool value{ true };
return value;
}
explicit constexpr operator bool() const noexcept { return m_value; }
private:
bool m_value;
};
int main()
{
constexpr TypedBool value{ true };
static_assert(value);
static_assert(TypedBool::yes());
return 0;
}
I think you can accomplish your goals (supporting ! operators and explicit conversion to bool) without changing your scoped enumerations.
All scoped enumerations appear to support explicit conversion to bool, even if bool isnt' the underlying type
enum class NotBool : int { No, Yes };
constexpr bool bad = NotBool::Yes; // compile error
constexpr bool yes = bool(NotBool::Yes);
You can overload the ! operator for all scoped enums that have underlying booleans with a template and std::enable_if:
template <typename T>
constexpr bool HasUnderlyingBool = std::is_same_v<std::underlying_type_t<T>, bool>;
template <typename T>
constexpr std::enable_if_t<HasUnderlyingBool<T>, T> operator !(const T& value) {
return T(!bool(value));
}
enum class Bool : bool { No, Yes };
static_assert(!Bool::Yes == Bool::No);
static_assert(!Bool::No == Bool::Yes);

enum size and initialization of variable [duplicate]

Is there a way to convert an enum class field to the underlying type? I thought this would be automatic, but apparently not.
enum class my_fields : unsigned { field = 1 };
unsigned a = my_fields::field;
That assignment is being rejected by GCC. error: cannot convert 'my_fields' to 'unsigned int' in assignment.
I think you can use std::underlying_type to know the underlying type, and then use cast:
#include <type_traits> //for std::underlying_type
typedef std::underlying_type<my_fields>::type utype;
utype a = static_cast<utype>(my_fields::field);
With this, you don't have to assume the underlying type, or you don't have to mention it in the definition of the enum class like enum class my_fields : int { .... } or so.
You can even write a generic convert function that should be able to convert any enum class to its underlying integral type:
template<typename E>
constexpr auto to_integral(E e) -> typename std::underlying_type<E>::type
{
return static_cast<typename std::underlying_type<E>::type>(e);
}
then use it:
auto value = to_integral(my_fields::field);
auto redValue = to_integral(Color::Red);//where Color is an enum class!
And since the function is declared to be constexpr, you can use it where constant expression is required:
int a[to_integral(my_fields::field)]; //declaring an array
std::array<int, to_integral(my_fields::field)> b; //better!
You cannot convert it implicitly, but an explicit cast is possible:
enum class my_fields : unsigned { field = 1 };
// ...
unsigned x = my_fields::field; // ERROR!
unsigned x = static_cast<unsigned>(my_fields::field); // OK
Also mind the fact, that the semicolon should be after the closed curly brace in your enum's definition, not before.
With C++23 you'll finally get a library function for this:
std::to_underlying
It is already implemented in the standard libraries of GCC 11, Clang 13, and MSVC 19.30 (aka 2022 17.0).
Until you're able to use C++23 I recommend you (re)name any custom implementation to to_underlying and place it between a #if !defined(__cpp_lib_to_underlying) #endif block, which is the associated feature test macro. This way you can simply ditch the code at some point in the future when C++23 becomes available for you.
As others have pointed out there is no implicit cast, but you can use an explicit static_cast. I use the following helper functions in my code to convert to and from an enum type and its underlying class.
template<typename EnumType>
constexpr inline decltype(auto) getIntegralEnumValue(EnumType enumValue)
{
static_assert(std::is_enum<EnumType>::value,"Enum type required");
using EnumValueType = std::underlying_type_t<EnumType>;
return static_cast<EnumValueType>(enumValue);
}
template<typename EnumType,typename IntegralType>
constexpr inline EnumType toEnum(IntegralType value)
{
static_assert(std::is_enum<EnumType>::value,"Enum type required");
static_assert(std::is_integral<IntegralType>::value, "Integer required");
return static_cast<EnumType>(value);
}
template<typename EnumType,typename UnaryFunction>
constexpr inline void setIntegralEnumValue(EnumType& enumValue, UnaryFunction integralWritingFunction)
{
// Since using reinterpret_cast on reference to underlying enum type is UB must declare underlying type value and write to it and then cast it to enum type
// See discussion on https://stackoverflow.com/questions/19476818/is-it-safe-to-reinterpret-cast-an-enum-class-variable-to-a-reference-of-the-unde
static_assert(std::is_enum<EnumType>::value,"Enum type required");
auto enumIntegralValue = getIntegralEnumValue(enumValue);
integralWritingFunction(enumIntegralValue);
enumValue = toEnum<EnumType>(enumIntegralValue);
}
Usage code
enum class MyEnum {
first = 1,
second
};
MyEnum myEnum = MyEnum::first;
std::cout << getIntegralEnumValue(myEnum); // prints 1
MyEnum convertedEnum = toEnum(1);
setIntegralEnumValue(convertedEnum,[](auto& integralValue) { ++integralValue; });
std::cout << getIntegralEnumValue(convertedEnum); // prints 2
I find the following function underlying_cast useful when having to serialise enum values correctly.
namespace util
{
namespace detail
{
template <typename E>
using UnderlyingType = typename std::underlying_type<E>::type;
template <typename E>
using EnumTypesOnly = typename std::enable_if<std::is_enum<E>::value, E>::type;
} // namespace util.detail
template <typename E, typename = detail::EnumTypesOnly<E>>
constexpr detail::UnderlyingType<E> underlying_cast(E e) {
return static_cast<detail::UnderlyingType<E>>(e);
}
} // namespace util
enum SomeEnum : uint16_t { A, B };
void write(SomeEnum /*e*/) {
std::cout << "SomeEnum!\n";
}
void write(uint16_t /*v*/) {
std::cout << "uint16_t!\n";
}
int main(int argc, char* argv[]) {
SomeEnum e = B;
write(util::underlying_cast(e));
return 0;
}

Retrieving the underlying type of a c++11 enum [duplicate]

Is there a way to convert an enum class field to the underlying type? I thought this would be automatic, but apparently not.
enum class my_fields : unsigned { field = 1 };
unsigned a = my_fields::field;
That assignment is being rejected by GCC. error: cannot convert 'my_fields' to 'unsigned int' in assignment.
I think you can use std::underlying_type to know the underlying type, and then use cast:
#include <type_traits> //for std::underlying_type
typedef std::underlying_type<my_fields>::type utype;
utype a = static_cast<utype>(my_fields::field);
With this, you don't have to assume the underlying type, or you don't have to mention it in the definition of the enum class like enum class my_fields : int { .... } or so.
You can even write a generic convert function that should be able to convert any enum class to its underlying integral type:
template<typename E>
constexpr auto to_integral(E e) -> typename std::underlying_type<E>::type
{
return static_cast<typename std::underlying_type<E>::type>(e);
}
then use it:
auto value = to_integral(my_fields::field);
auto redValue = to_integral(Color::Red);//where Color is an enum class!
And since the function is declared to be constexpr, you can use it where constant expression is required:
int a[to_integral(my_fields::field)]; //declaring an array
std::array<int, to_integral(my_fields::field)> b; //better!
You cannot convert it implicitly, but an explicit cast is possible:
enum class my_fields : unsigned { field = 1 };
// ...
unsigned x = my_fields::field; // ERROR!
unsigned x = static_cast<unsigned>(my_fields::field); // OK
Also mind the fact, that the semicolon should be after the closed curly brace in your enum's definition, not before.
With C++23 you'll finally get a library function for this:
std::to_underlying
It is already implemented in the standard libraries of GCC 11, Clang 13, and MSVC 19.30 (aka 2022 17.0).
Until you're able to use C++23 I recommend you (re)name any custom implementation to to_underlying and place it between a #if !defined(__cpp_lib_to_underlying) #endif block, which is the associated feature test macro. This way you can simply ditch the code at some point in the future when C++23 becomes available for you.
As others have pointed out there is no implicit cast, but you can use an explicit static_cast. I use the following helper functions in my code to convert to and from an enum type and its underlying class.
template<typename EnumType>
constexpr inline decltype(auto) getIntegralEnumValue(EnumType enumValue)
{
static_assert(std::is_enum<EnumType>::value,"Enum type required");
using EnumValueType = std::underlying_type_t<EnumType>;
return static_cast<EnumValueType>(enumValue);
}
template<typename EnumType,typename IntegralType>
constexpr inline EnumType toEnum(IntegralType value)
{
static_assert(std::is_enum<EnumType>::value,"Enum type required");
static_assert(std::is_integral<IntegralType>::value, "Integer required");
return static_cast<EnumType>(value);
}
template<typename EnumType,typename UnaryFunction>
constexpr inline void setIntegralEnumValue(EnumType& enumValue, UnaryFunction integralWritingFunction)
{
// Since using reinterpret_cast on reference to underlying enum type is UB must declare underlying type value and write to it and then cast it to enum type
// See discussion on https://stackoverflow.com/questions/19476818/is-it-safe-to-reinterpret-cast-an-enum-class-variable-to-a-reference-of-the-unde
static_assert(std::is_enum<EnumType>::value,"Enum type required");
auto enumIntegralValue = getIntegralEnumValue(enumValue);
integralWritingFunction(enumIntegralValue);
enumValue = toEnum<EnumType>(enumIntegralValue);
}
Usage code
enum class MyEnum {
first = 1,
second
};
MyEnum myEnum = MyEnum::first;
std::cout << getIntegralEnumValue(myEnum); // prints 1
MyEnum convertedEnum = toEnum(1);
setIntegralEnumValue(convertedEnum,[](auto& integralValue) { ++integralValue; });
std::cout << getIntegralEnumValue(convertedEnum); // prints 2
I find the following function underlying_cast useful when having to serialise enum values correctly.
namespace util
{
namespace detail
{
template <typename E>
using UnderlyingType = typename std::underlying_type<E>::type;
template <typename E>
using EnumTypesOnly = typename std::enable_if<std::is_enum<E>::value, E>::type;
} // namespace util.detail
template <typename E, typename = detail::EnumTypesOnly<E>>
constexpr detail::UnderlyingType<E> underlying_cast(E e) {
return static_cast<detail::UnderlyingType<E>>(e);
}
} // namespace util
enum SomeEnum : uint16_t { A, B };
void write(SomeEnum /*e*/) {
std::cout << "SomeEnum!\n";
}
void write(uint16_t /*v*/) {
std::cout << "uint16_t!\n";
}
int main(int argc, char* argv[]) {
SomeEnum e = B;
write(util::underlying_cast(e));
return 0;
}

Constexpr member function

Suppose I have a struct template S that is parametrized by an engine:
template<class Engine> struct S;
I have two engines: a "static" one with a constexpr member function size(), and a "dynamic" one with a non-constexpr member function size():
struct Static_engine {
static constexpr std::size_t size() {
return 11;
}
};
struct Dynamic_engine {
std::size_t size() const {
return size_;
}
std::size_t size_ = 22;
};
I want to define size() member function in S that can be used as a constexpr if the engine's size() is constexpr. I write:
template<class Engine>
struct S {
constexpr std::size_t size() const {
return engine_.size();
}
Engine engine_;
};
Then the following code compiles with GCC, Clang, MSVC and ICC:
S<Static_engine> sta; // not constexpr
S<Dynamic_engine> dyn;
constexpr auto size_sta = sta.size();
const auto size_dyn = dyn.size();
Taking into account intricacies of constexpr and various "ill-formed, no diagnostic is required", I still have the question: is this code well-formed?
Full code on Godbolt.org
(I tagged this question with both c++17 and c++20 in case this code has different validity in these two standards.)
The code is fine as written.
[dcl.constexpr]
6 If the instantiated template specialization of a constexpr
function template or member function of a class template would fail to
satisfy the requirements for a constexpr function or constexpr
constructor, that specialization is still a constexpr function or
constexpr constructor, even though a call to such a function cannot
appear in a constant expression. If no specialization of the template
would satisfy the requirements for a constexpr function or constexpr
constructor when considered as a non-template function or constructor,
the template is ill-formed, no diagnostic required.
The member may not appear in a constant expression for the specialization that uses Dynamic_engine, but as the paragraph above details, that does not make S::size ill-formed. We are also far from ill-formed NDR territory, since valid instantations are possible. Static_engine being a prime example.
The quote is from n4659, the last C++17 standard draft, and similar wording appears in the latest C++20 draft.
As for the evaluation of sta.size() as a constant expression, going over the list at [expr.const] I cannot find anything that is disallowed in the evaluation itself. It is therefore a valid constant expression (because the list tells us what isn't valid). And in general for a constexpr function to be valid, there just needs to exist some set of arguments for which the evaluation produces a valid constant expression. As the following example form the standard illustrates:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
struct B {
constexpr B(int x) : i(0) { } // x is unused
int i;
};
int global;
struct D : B {
constexpr D() : B(global) { } // ill-formed, no diagnostic required
// lvalue-to-rvalue conversion on non-constant global
};
Yes.
Functions may be marked as constexpr without being forced to be evaluated at compile-time. So long as you satisfy the other requirements for marking a function as constexpr, things are okay (returns a literal type, parameters are literals, no inline asm, etc.). The only time you may run into issues is if it's not actually possible to create arguments that satisfy the function being called as a core constant expression. (e.g., if your function had undefined behavior for all values, then your function would be ill-formed NDR)
In C++20, we received the consteval specifier that forces all calls to the function to be able to produce a compile-time constant (constexpr).
Not a direct answer but an alternative way:
struct Dynamic_Engine
{
using size_type = size_t;
size_type size() const
{
return _size;
}
size_type _size = 22;
};
struct Static_Engine
{
using size_type = std::integral_constant<size_t, 11>;
size_type size() const
{
return size_type();
}
};
template <typename ENGINE>
struct S
{
auto size() const
{
return _engine.size();
}
ENGINE _engine;
};
int main()
{
S<Static_Engine> sta;
S<Dynamic_Engine> dyn;
const auto size_sta = sta.size();
const auto size_dyn = dyn.size();
static_assert(size_sta == 11);
}
I had the same kind of problems and IMHO the easiest and more versatile solution is to use std::integral_constant. Not more needs to juggle with constexpr as the size information is directly encoded into the type
If you still really want to use constexpr (with its extra complications) you can do:
struct Dynamic_Engine
{
size_t size() const
{
return _size;
}
size_t _size = 22;
};
struct Static_Engine
{
static constexpr size_t size() // note: static
{
return 11;
}
};
template <typename ENGINE>
struct S
{
constexpr size_t size() const
{
return _engine.size();
}
ENGINE _engine;
};
int main()
{
S<Static_Engine> sta;
S<Dynamic_Engine> dyn;
constexpr size_t size_sta = sta.size();
const size_t size_dyn = dyn.size();
static_assert(size_sta == 11);
}

Resolving or aliasing enum types variable to primitive data types in function template parameters [duplicate]

I have an enum like:
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
}
And I want to create a compile-time mapping to get the appropriate E for a type like:
GetE<float> // returns TYPE_FLOAT
GetE<char> // returns TYPE_CHAR
GetE<int> // returns TYPE_INT
I thought of:
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
But I'm getting errors like:
undefined reference to `GetE<int>::type'
Whats the best way to do this? And why the error?
It depends on how you use these constant expressions.
The ODR (one-definition rule) states that
(§3.2/2) [...] A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. [...]
(And then, lots of special rules, exceptions and exceptions of the exceptions follow.)
Any variable that is odr-used, must have exactly one definition. Your constant expressions have a declaration, but not a definition, so this goes well unless you odr-use one of them.
For example, the following goes well:
int main() {
E e = GetE<float>::type;
return 0;
}
But this does not:
void f(const E &)
{ }
int main() {
f(GetE<float>::type);
return 0;
}
because f requires a (const) reference, so the lvalue-to-rvalue conversion cannot be applied immediately, hence this constitutes an odr-use. The compiler will complain that it misses a definition.
(Remark. As ShafikYaghmour found (see the comments), you may not get a complaint if the compiler uses optimization, as the references may be optimized away. To reproduce the compiler complaint, use the -O0 flag (or similar, depending on the compiler).)
To solve the problem, the required definition can be provided in the usual way, i.e. outside the struct-definition:
constexpr E GetE<float>::type;
constexpr E GetE<char>::type;
constexpr E GetE<int>::type;
But since this would have to happen in the .cpp (not the header file), you'll end up having to maintain the declarations and definitions in two different places, which is cumbersome.
The solution you've just suggested in your comment, i.e. define a constexpr (and inline) function, sounds right:
template <class T> constexpr E GetE();
template <> constexpr E GetE<float>()
{ return TYPE_FLOAT; }
template <> constexpr E GetE<char>()
{ return TYPE_CHAR; }
template <> constexpr E GetE<int>()
{ return TYPE_INT; }
void f(const E &)
{ }
int main() {
E e = GetE<float>();
f(GetE<float>());
return 0;
}
Static member variables need to be defined outside the class scope:
class C {
const static int x = 5;
};
decltype(C::x) C::x;
Maybe because you forgot to put a semicolon after the enum definition, this works for me in LiveWorkSpace:
#include <iostream>
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
} ;
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
int main()
{
std::cout << GetE<int>::type << std::endl ;
}
here is a link to the code http://liveworkspace.org/code/nHqUe$6