wise_enum
wise_enum copied to clipboard
wise_enum::size compile error
I'm trying to use wise_enum::size
like this:
class MyClass final {
public:
WISE_ENUM_MEMBER(Parameter, First, Second)
array<Foo, wise_enum::size<Parameter>> values;
};
Unfortunately, I'm getting a compiler error on this line saying: "Constexpr variable 'range' must be initialized by a constant expression" and "Undefined function 'wise_enum_detail_array' cannot be used in a constant expression".
wise_enum_detail_array
seems to be defined through the WISE_ENUM_IMPL_ADAPT_3
macro, but I couldn't figure out what exactly is going wrong there..
Any help would be much appreciated! 😊 I'm using Xcode Version 10.1 (10B61) with C++14 standard, with wise_enum at the latest master commit.
Can you reproduce this on clang on a linux system? To me it looks like a compiler bug but it's hard for me to be sure without reproducing.
Unfortunately I don't have a linux system set up (or a VM), so I can't test this..for now I'm just using the old enum trick of adding a NumParameters
case at the end.
I realize this thread is over a year old, but I encountered what seems to be the same error, so I thought I'd comment here.
I'm using Xcode 12.1, with C++ 17 (not GNU), with the latest version of Wise Enum as of this writing. This test code:
#include <array>
#include "wise_enum.h"
class Test {
WISE_ENUM_CLASS_MEMBER(Enum, a, b, c)
std::array<int, wise_enum::size<Enum>> array;
};
int main(int argc, char * argv[]) {
}
Seems to generate errors similar to those mentioned by @martinfinke, including:
"Constexpr variable 'range' must be initialized by a constant expression"
At wise_enum.h, line 63.
I hope this information will be of use.
The following code:
#include "wise_enum.h"
namespace udaq::udaq
{
class Test {
WISE_ENUM_CLASS_MEMBER(Enums, a, b, c)
std::array<int, wise_enum::size<Enums>> array;
};
}
Fails with the following when compiled with gcc 10.2.1 x64 on Debian :
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h: In instantiation of ‘constexpr const std::array<wise_enum::detail::value_and_name<udaq::udaq::Test::Enums>, 3> wise_enum::enumerators<udaq::udaq::Test::Enums>::range’:
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:41: required from ‘constexpr const size_t wise_enum::enumerators<udaq::udaq::Test::Enums>::size’
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:46: required from ‘constexpr const size_t wise_enum::size<udaq::udaq::Test::Enums>’
/mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:36: required from here
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:64:29: error: ‘constexpr std::array<wise_enum::detail::value_and_name<udaq::udaq::Test::Enums>, 3> udaq::udaq::wise_enum_detail_array(wise_enum::detail::Tag<udaq::udaq::Test::Enums>)’ called in a constant expression before its definition is complete
64 | wise_enum_detail_array(detail::Tag<T>{});
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
make[3]: *** [udaq/CMakeFiles/udaq.dir/build.make:82: udaq/CMakeFiles/udaq.dir/src/test.cpp.o] Error 1
And the following error when compiled with clang 11.0.1-2 on the same box:
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:63:71: error: constexpr variable 'range' must be initialized by a constant expression
static constexpr decltype(wise_enum_detail_array(detail::Tag<T>{})) range =
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:41: note: in instantiation of static data member 'wise_enum::enumerators<udaq::udaq::Test::Enums>::range' requested here
static constexpr std::size_t size = range.size();
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:46: note: in instantiation of static data member 'wise_enum::enumerators<udaq::udaq::Test::Enums>::size' requested here
constexpr std::size_t size = enumerators<T>::size;
^
/mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:36: note: in instantiation of variable template specialization 'wise_enum::size<udaq::udaq::Test::Enums>' requested here
std::array<int, wise_enum::size<Enums>> array;
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:64:7: note: undefined function 'wise_enum_detail_array' cannot be used in a constant expression
wise_enum_detail_array(detail::Tag<T>{});
^
/mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:6:9: note: declared here
WISE_ENUM_CLASS_MEMBER(Enums, a, b, c)
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:39:3: note: expanded from macro 'WISE_ENUM_CLASS_MEMBER'
WISE_ENUM_IMPL(enum class, name, friend, __VA_ARGS__)
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum_detail.h:164:3: note: expanded from macro 'WISE_ENUM_IMPL'
WISE_ENUM_IMPL_2(type, WISE_ENUM_IMPL_ONLY_OR_FIRST(name_storage), \
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum_detail.h:169:3: note: expanded from macro 'WISE_ENUM_IMPL_2'
WISE_ENUM_IMPL_3(type, name, storage, friendly, num_enums, \
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum_detail.h:178:3: note: expanded from macro 'WISE_ENUM_IMPL_3'
WISE_ENUM_IMPL_ADAPT_3(name, friendly, num_enums, loop, __VA_ARGS__)
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum_detail.h:195:3: note: expanded from macro 'WISE_ENUM_IMPL_ADAPT_3'
wise_enum_detail_array(::wise_enum::detail::Tag<name>) { \
^
In file included from /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:1:
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:34: error: constexpr variable 'size' must be initialized by a constant expression
static constexpr std::size_t size = range.size();
^ ~~~~~~~~~~~~
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:46: note: in instantiation of static data member 'wise_enum::enumerators<udaq::udaq::Test::Enums>::size' requested here
constexpr std::size_t size = enumerators<T>::size;
^
/mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:36: note: in instantiation of variable template specialization 'wise_enum::size<udaq::udaq::Test::Enums>' requested here
std::array<int, wise_enum::size<Enums>> array;
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:47: note: initializer of 'range' is not a constant expression
static constexpr std::size_t size = range.size();
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:63:71: note: declared here
static constexpr decltype(wise_enum_detail_array(detail::Tag<T>{})) range =
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:23: error: constexpr variable 'size<udaq::udaq::Test::Enums>' must be initialized by a constant expression
constexpr std::size_t size = enumerators<T>::size;
^ ~~~~~~~~~~~~~~~~~~~~
/mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:36: note: in instantiation of variable template specialization 'wise_enum::size<udaq::udaq::Test::Enums>' requested here
std::array<int, wise_enum::size<Enums>> array;
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:30: note: initializer of 'size' is not a constant expression
constexpr std::size_t size = enumerators<T>::size;
^
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:34: note: declared here
static constexpr std::size_t size = range.size();
^
/mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:25: error: non-type template argument is not a constant expression
std::array<int, wise_enum::size<Enums>> array;
^~~~~~~~~~~~~~~~~~~~~~
/mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:25: note: initializer of 'size<udaq::udaq::Test::Enums>' is not a constant expression
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:23: note: declared here
constexpr std::size_t size = enumerators<T>::size;
^
4 errors generated.
The following code:
#include "wise_enum.h" namespace udaq::udaq { class Test { WISE_ENUM_CLASS_MEMBER(Enums, a, b, c) std::array<int, wise_enum::size<Enums>> array; }; }
Fails with the following when compiled with gcc 10.2.1 x64 on Debian :
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h: In instantiation of ‘constexpr const std::array<wise_enum::detail::value_and_name<udaq::udaq::Test::Enums>, 3> wise_enum::enumerators<udaq::udaq::Test::Enums>::range’: /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:41: required from ‘constexpr const size_t wise_enum::enumerators<udaq::udaq::Test::Enums>::size’ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:46: required from ‘constexpr const size_t wise_enum::size<udaq::udaq::Test::Enums>’ /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:36: required from here /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:64:29: error: ‘constexpr std::array<wise_enum::detail::value_and_name<udaq::udaq::Test::Enums>, 3> udaq::udaq::wise_enum_detail_array(wise_enum::detail::Tag<udaq::udaq::Test::Enums>)’ called in a constant expression before its definition is complete 64 | wise_enum_detail_array(detail::Tag<T>{}); | ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ make[3]: *** [udaq/CMakeFiles/udaq.dir/build.make:82: udaq/CMakeFiles/udaq.dir/src/test.cpp.o] Error 1
And the following error when compiled with clang 11.0.1-2 on the same box:
/mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:63:71: error: constexpr variable 'range' must be initialized by a constant expression static constexpr decltype(wise_enum_detail_array(detail::Tag<T>{})) range = ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:41: note: in instantiation of static data member 'wise_enum::enumerators<udaq::udaq::Test::Enums>::range' requested here static constexpr std::size_t size = range.size(); ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:46: note: in instantiation of static data member 'wise_enum::enumerators<udaq::udaq::Test::Enums>::size' requested here constexpr std::size_t size = enumerators<T>::size; ^ /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:36: note: in instantiation of variable template specialization 'wise_enum::size<udaq::udaq::Test::Enums>' requested here std::array<int, wise_enum::size<Enums>> array; ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:64:7: note: undefined function 'wise_enum_detail_array' cannot be used in a constant expression wise_enum_detail_array(detail::Tag<T>{}); ^ /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:6:9: note: declared here WISE_ENUM_CLASS_MEMBER(Enums, a, b, c) ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:39:3: note: expanded from macro 'WISE_ENUM_CLASS_MEMBER' WISE_ENUM_IMPL(enum class, name, friend, __VA_ARGS__) ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum_detail.h:164:3: note: expanded from macro 'WISE_ENUM_IMPL' WISE_ENUM_IMPL_2(type, WISE_ENUM_IMPL_ONLY_OR_FIRST(name_storage), \ ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum_detail.h:169:3: note: expanded from macro 'WISE_ENUM_IMPL_2' WISE_ENUM_IMPL_3(type, name, storage, friendly, num_enums, \ ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum_detail.h:178:3: note: expanded from macro 'WISE_ENUM_IMPL_3' WISE_ENUM_IMPL_ADAPT_3(name, friendly, num_enums, loop, __VA_ARGS__) ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum_detail.h:195:3: note: expanded from macro 'WISE_ENUM_IMPL_ADAPT_3' wise_enum_detail_array(::wise_enum::detail::Tag<name>) { \ ^ In file included from /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:1: /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:34: error: constexpr variable 'size' must be initialized by a constant expression static constexpr std::size_t size = range.size(); ^ ~~~~~~~~~~~~ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:46: note: in instantiation of static data member 'wise_enum::enumerators<udaq::udaq::Test::Enums>::size' requested here constexpr std::size_t size = enumerators<T>::size; ^ /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:36: note: in instantiation of variable template specialization 'wise_enum::size<udaq::udaq::Test::Enums>' requested here std::array<int, wise_enum::size<Enums>> array; ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:47: note: initializer of 'range' is not a constant expression static constexpr std::size_t size = range.size(); ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:63:71: note: declared here static constexpr decltype(wise_enum_detail_array(detail::Tag<T>{})) range = ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:23: error: constexpr variable 'size<udaq::udaq::Test::Enums>' must be initialized by a constant expression constexpr std::size_t size = enumerators<T>::size; ^ ~~~~~~~~~~~~~~~~~~~~ /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:36: note: in instantiation of variable template specialization 'wise_enum::size<udaq::udaq::Test::Enums>' requested here std::array<int, wise_enum::size<Enums>> array; ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:30: note: initializer of 'size' is not a constant expression constexpr std::size_t size = enumerators<T>::size; ^ /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:67:34: note: declared here static constexpr std::size_t size = range.size(); ^ /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:25: error: non-type template argument is not a constant expression std::array<int, wise_enum::size<Enums>> array; ^~~~~~~~~~~~~~~~~~~~~~ /mnt/c/Saman/Code/C++/udaq/udaq/src/test.cpp:7:25: note: initializer of 'size<udaq::udaq::Test::Enums>' is not a constant expression /mnt/c/Saman/Code/C++/udaq/external/wise_enum/wise_enum.h:82:23: note: declared here constexpr std::size_t size = enumerators<T>::size; ^ 4 errors generated.
For what it's worth, although I'm not positive, I think this may be an inherent limitation of the approach the library uses, for reasons discussed here.
Sorry I've let this linger so long; I'm going to take a look at it. It may indeed be a basic limitation of the approach as @jesse-k said. Over the years, I've encountered quite a few nasty edge cases in C++ that have led me to try to avoid nested classes in general, or at least, just give up quickly at the first sign of trouble. I'd probably just recommend defining the enum in a detail namespace and using an alias, an approach that should have very few practical downsides. But I will try to see if this is possible to fix. The first step I think will be coming up with a repro without using any macros to fully understand the issue.
Okay, I reproduced this with simply:
#include <array>
namespace lib {
template <class T>
struct tag{};
template <class T>
constexpr inline auto range = detail_array(tag<T>{});
template <class T>
constexpr inline auto size = range<T>.size();
}
struct foo {
enum class bar { FIRST, SECOND };
constexpr friend std::array<bar, 2> detail_array(lib::tag<bar>) { return { bar::FIRST, bar::SECOND}; }
std::array<int, lib::size<bar>> x; // doesn't compile
};
int main() {
constexpr auto y = lib::size<foo::bar>; // works fine
}
It seems like the compiler is correct to reject this code: "Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.", http://eel.is/c++draft/temp#point-4.sentence-2. Basically, when you try to declare the x
member variable, it instantiates lib::size
and that template is looked up from before foo
is created, so detail_array doesn't exist yet.
So, indeed, it is just a limitation of the approach, although, it seems like almost any generic, free function based approach in C++ will have the exact same problem. At least, I can't imagine how it would avoid it.
The only question I guess is whether to document this behavior, or simply remove support for member enum classes. I'm leaning towards the former. I welcome feedback on this issue from anyone in the thread; I'll close this issue once I've decided and implemented.
The only question I guess is whether to document this behavior, or simply remove support for member enum classes. I'm leaning towards the former. I welcome feedback on this issue from anyone in the thread; I'll close this issue once I've decided and implemented.
Since I was a participant in the thread, I'll go ahead and offer that documenting it as a limitation seems reasonable (the code wouldn't have to be changed then, and support for member enums can be useful, despite the limitations).
Agreed, I think documenting it as a limitation is the way to go.
+1 for documenting, but leaving support for member enums. Thank you for taking the time to look at this @quicknir ! And also thank you @samangh and @jesse-k for narrowing down the problem.
Update on this, it's actually possible to partly workaround this limitation. While fully reflection in the class body is impossible, at least accessing the size is actually possible, because it can be transmitted via the type (at least, that is my understanding, I don't claim to fully understand this). So, if I change the definition of size to:
template <class T>
constexpr inline auto size = std::tuple_size<decltype(detail_array(tag<T>{}))>::value;
Then my example above seems to work. I'll test this within the actual library next, and see if something breaks. Offhand, I don't think there's any major downside here in terms of compile time, may make errors a little more confusing, but it seems like a worthwhile tradeoff.
Update on this, it's actually possible to partly workaround this limitation.
@quicknir Great work! Thank you 🎉 I can confirm that your solution works on my end as well, both on Clang/Xcode and in Visual Studio 2019. I can have a std::array
member with the range/size of a nested enum.