aantron / better-enums Goto Github PK
View Code? Open in Web Editor NEWC++ compile-time enum to string, iteration, in a single header file
Home Page: http://aantron.github.io/better-enums
License: BSD 2-Clause "Simplified" License
C++ compile-time enum to string, iteration, in a single header file
Home Page: http://aantron.github.io/better-enums
License: BSD 2-Clause "Simplified" License
better-enums uses some identifiers which are not allowed according to the C++ standard. Identifiers beginning with an underscore followed by an uppercase letter are reserved in any namespace (ref). better-enums uses three of those:
_Iterable
_PutNamesInThisScopeAlso
_EnumClassForSwitchStatements
What makes it worse is that these identifiers are not only in library headers, but are inserted into all users' source files via macros. Therefore, code quality checkers flag all sources using better-enums as violating standards.
Currently, I'm using better enums for the enums I need to be able to iterate over. It works excellent for that. The only issue is I'm using a separate enum for converting cstrings to enums with rt_crc32.
I noticed there is _from_string_nocase()
, which works except I used underscores between words in my enum. It doesn't look like make_map
is able to go from a string to the enum representation. The string I'm passing in doesn't have the underscore between the two words. Is there anyway to map string to enum, like enum to string has?
I would be nice to have a traits class to detect whether a type is a "better enum" (akin to std::is_enum
)
Hi, what would be your approach to attach metadata (such as descriptions, alternate representations ...) to each enum value ?
Example 1:
BETTER_ENUM(Direction, int, {
{up, "Upwards"},
{down, "Downwards"},
{left, "Leftwards"},
{right, "Rightwards"}
})
Direction d = Direction::up;
std::cout << d._meta(); // Upwards
Example 2:
BETTER_ENUM(Color, int, {
{AliceBlue, {"#F0F8FF", 215, 223, 236}},
{Aqua, {"#00FFFF", 0, 255, 255}},
...})
Color c = Color::Aqua;
std::cout << c._meta()[0]; // #00FFFF
I want to use this library but it is important that the enums are documentable.
I can document the macro invocation, and even fake parameter values
BETTER_ENUM(A, uint8_t,
Thing1,
Thing2
); ///< \brief Comment about enum
///< \param Thing1 the first thing
but these are not searchable.
I've tried to document the class that is generated under the hood
///\class A
///\enum A::_enumerated
///\var A::_enumerated A::Thing1 the first thing
but doxygen, expanding macros, cannot find either the class or the enum.
Any suggestions on how to proceed or am I SOL?
The markdown files in /tutorial and /doc contain html tags and placeholder variables such as ${prefix} that are used to generate the online project page. Sadly, the tags are fully visible and it breakes all links when viewed on github.
Example taken directly from the constexpr tutorial:
#include <iostream>
// The reason for this is explained below.
#ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING
#define BETTER_ENUMS_CONSTEXPR_TO_STRING
#endif
<em>#include <enum.h></em>
<em>ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Red</em> = <em>1</em>, <em>Green</em> = <em>2</em>, <em>Blue</em> = <em>3</em>)
<em>constexpr</em> Channel channel = <em>Channel::_from_integral(2)</em>;
<em>constexpr</em> int value = <em>channel._to_integral()</em>;
<em>constexpr</em> const char *name = <em>channel._to_string()</em>;
<em>constexpr</em> Channel parsed = <em>Channel::_from_string("Red")</em>;
It would be nice if users could properly view the docs directly on the project page on github or use it as offline documentation. I am not sure if this is possible without breaking http://aantron.github.io/better-enums.
I'm not sure if you support this, but for serialization purposes, it would be nice for case insensitivity when converting from string to enum.
Basically, a string "FOO"
should map to an enumerator Foo
.
This could be a case of missing something in the documentation but I couldn't find any mention that comparisons don't work as you'd typically expect.
That is, the following will fail with an overload resolution error:
BETTER_ENUM(Channel, int, Red, Green, Blue)
Channel channel = Channel::Red;
if(channel == Channel::Red) // channel.value_ is fine
I'll admit to not going through all of the documentation (tried a search and a few examples) since I wanted a quick evaluation of the library and I'm not interested in the more advanced capabilities but this seems like something that, if it can't be fixed, should be made fairly clear.
Thanks!
I want to make a role field for a user. So I try to compile this code:
struct User{
BETTER_ENUM(Role, int,
user,
driver)
};
and it tells me Missing '}' at end of definition of 'User'
at the first line and Extraneous closing brace ('}')
at the last line. Compiling using Xcode 7.3.1. The issue is how to create enum enclosed inside a class?
switch()
doesn't seem to work when combined with BETTER_ENUMS_STRICT_CONVERSION
. This is because it's using BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE_GENERATE
, which creates a "copy" scoped enum with the same enumerators. The problem is that enumerators from _enumerated
(which are also specified in each case
section) cannot be compared to the like-named enumerators in the "copy" version.
Any reason that in the strict case, the implicit typecast operator in the Enum
class isn't just returning _enumerated
and not this dummy scoped enum? Oddly though, the code compiles fine on MSVC 14 but on Android NDK GCC 4.9, it does not work:
MSRData.cpp: In constructor 'CMSRData::CMSRData(const char*, MsrEncryptionStatus)':
MSRData.cpp:42:36: error: could not convert 'Ksn' from 'MsrEncryptionStatus::_enumerated' to 'better_enums::_data_MsrEncryptionStatus::_EnumClassForSwitchStatements'
case MsrEncryptionStatus::Ksn:
^
MSRData.cpp:51:36: error: could not convert 'None' from 'MsrEncryptionStatus::_enumerated' to 'better_enums::_data_MsrEncryptionStatus::_EnumClassForSwitchStatements'
case MsrEncryptionStatus::None:
^
MSRData.cpp:55:36: error: could not convert 'Dukpt' from 'MsrEncryptionStatus::_enumerated' to 'better_enums::_data_MsrEncryptionStatus::_EnumClassForSwitchStatements'
case MsrEncryptionStatus::Dukpt:
^
It seems it cannot, As soon as I declare a member variable of a BETTER_ENUM, say Colour, the compiler says:
error: ‘Colour::Colour()’ is private within this context
and the constructor of the class where it is declared becomes deleted.
Any ideas?
It seems to be suffering from some bit rot, and Clang 3.3 is a pretty old version. Clang 3.3 may be mentioned by the docs, so that needs to be changed, too.
Hi.
Visual Studio 2017:
If I hover a instance of a normal enum type the name and the value are displayed.
If I hover a instance of a better enum type only the value is displayed.
Is it possible to display also the name on better enums?
Eclipst CDT complains about the default constructor for class Enum - it gives a warning about the lack of explicit initialization of all data members.
Assuming no negative reprecussions, I suggest replacing this:
#ifndef BETTER_ENUMS_DEFAULT_CONSTRUCTOR
# define BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum) \
private: \
Enum() { }
#endif
with this:
#ifndef BETTER_ENUMS_DEFAULT_CONSTRUCTOR
# define BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum) \
private: \
Enum() : _value() { }
#endif
i.e. explicitly default-initializing the _value()
field.
BETTER_ENUM(EnumTest, uint32, NONE = 0, APPLE)
class Apple
{
public:
Apple() : test(EnumTest::APPLE) {}
Apple(int k) {}
EnumTest test;
};
BETTER_ENUM(EnumTest, uint32, NONE = 0, APPLE)
class Apple
{
public:
Apple() : test(EnumTest::APPLE) {}
Apple(int k) : test(EnumTest::APPLE) {}
EnumTest test;
};
or
Thanks.
At the moment, the _from_string()
function, its variants, and some additional functions such as _is_valid()
take their input string as a const char*
, meaning that you can't pass an std::string
directly. I would expect to be able to do this without any conversions, wtih std::string being the preferred type for (a lot of / most) string work in C++.
If this is not intentional, I suggest variants be added which take const std::string&
parameters.
Visual Studio 2015 added support for constexpr
, but the implementation seems too buggy to get it working with Better Enums without a bunch of workarounds. Progress is in the branch vs2015-constexpr
. Internal compiler errors can be seen here – search for "error" in the log.
Why is Better Enums using the underscore character '_'
as a prefix for all functions? It is forbid in the global namespace by the C++ standard only and I think Better Enums only uses this in the better_enum
namespace, so it should be no problem. Nonetheless I ask myself what the intention behind that naming convention is.
Isn't Channel::from_string("Blue")
more readable than Channel::_from_string("Blue")
?
The first character of an identifier must be an alphabetic character, either uppercase or lowercase, or an underscore ( _ ). Because C++ identifiers are case sensitive, fileName is different from FileName.
Additional resource: https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier
Hello @aantron & co.,
Trying to compile with BETTER_ENUM in Visual Studio 2017 produces the following error:
E0028
expression must have a constant value
attempt to access run-time storage
The error occurs at the assignment of _value_array.
Here are a few test cases of the compiler behavior:
//These compile
constexpr const int example0[] = {
123
};
constexpr const MyEnum example1[] = {
MyEnum::MyEnumValue
};
constexpr const int myEnumValue = MyEnum::MyEnumValue;
constexpr const int example2[] = {
myEnumValue
};
//These array assignments all produce error E0028
constexpr const MyEnum example3[] = {
((::better_enums::_eat_assign<MyEnum>)MyEnum::MyEnumValue = 0)
};
constexpr const MyEnum example4[] = {
::better_enums::_eat_assign<MyEnum>(MyEnum::MyEnumValue)
};
constexpr ::better_enums::_eat_assign<MyEnum> myEat(MyEnum::MyEnumValue);
constexpr const MyEnum example5[] = {
myEat
};
This is in Visual Studio 2017 (v15.4.5), and happens using any of the ISO C++14, ISO C++17, and Latest Draft compilers.
I know this is intended to work (apropos the recent commit to support constexpr in VS2017). What's likely to be the issue?
❤️ ,
dan
Figured I'd open an issue for this, since it was discussed in multiple places (#22, #23).
I have another suggestion besides making "strict conversions" the default: eliminate implicit conversions to enums completely. IIRC they are only needed for switch
, for which we could provide a function with a name like _switch
:
switch(my_enum._switch()) {
case Foo::A: break;
case Foo::B: break;
}
We could also make this operator +
, if you can bear all the operator +
s :)
switch(+my_enum) { ...
The conversion this would perform would be to an unscoped enum type, so we could use the Foo::A
syntax in cases without having to prepend the existing operator +
.
You should put enum.h
inside an include/betterenums
subdirectory of your Git repository. This would allow superproject build scripts to clone your Git repo as-is and add -I superproject/thirdpartylibs/betterenums/include
compiler flags where appropriate.
To include your header, we would use
#include <betterenums/enum.h>
which would avoid collisions with other headers named enum.h
.
I exceeded the implicit enum size limit so had to extend the BETTER_ENUMS__PP_COUNT-etc macros to support more than 64 elements ... perhaps 128 or something might be a good new limit for example.
I see Better Enums is a nice project and I'd like using it to define a set of measurement units.
The problem is that some of them (m/s, km/h, ...) contain a '/' character in their string representation.
Is there a way to handle this in Better Enum?
See old discussion of tradeoffs between traits and objects. Objects have some additional flaws.
Traits branch: https://github.com/aantron/better-enums/tree/traits
Declaring a Better Enum as a member variable in a class without a user-defined constructor results in a C2248 compiler error (tested with both MSVC 12.0 + MSVC 14.0, using /W3
).
The C2248 error does not occur if the default constructor is generated by the compiler (and no other constructors are defined).
#include <iostream>
#include <enum.h>
namespace example {
BETTER_ENUM(Boolean, bool, TRUE = true, FALSE = false)
class Example {
public:
Example() {
// NOOP
}
// 1. Workaround
// Example() : boolean_enum_{Boolean::FALSE} {
// // NOOP
// }
// Microsoft Visual C++ Compiler Error C2248.
Boolean boolean_enum_;
// 2. Workaround
// Boolean boolean_enum_{Boolean::FALSE};
};
} // namespace example
int main() {
example::Example example{};
std::cout << example.boolean_enum_._to_string();
}
Fehler: C2248: "example::Boolean::Boolean": Kein Zugriff auf private Member, dessen Deklaration in der example::Boolean-Klasse erfolgte.
I think Better Enums should work the same as a built-in enum class
. Due to this error I cannot use Better Enums in production code. This is a severe issue, since users have to modify client code if upgrading from built-in enumerations to Better Enums.
There are at least two possible workaround:
How do I use the BETTER_ENUM macro if I need to add declspec(_dllexport) ?
The following does not compile with MSVC 9.0:
ENUM(immersion_medium, int, unknown = -1, oil = 0, water, dry, silicone, glycerol)
int _tmain(int argc, _TCHAR* argv[])
{
immersion_medium::_from_integral( 0 ) ;
return 0;
}
error LNK2019: unresolved external symbol...
A copy constructor seems to be missing.
Hi
just a question: can such conversions be disabled somehow (like with enum class from c++11)
int a = Words::Hello;
This would be very useful - even if it is enabled only with a define before the inclusion of the enum header.
Also with enum classes we can forward declare them and even make a value out of them (if not using the enum values)
enum class A;
A op;
and with your library an enum can be forward declared like this class Words;
but cannot be used for a value - only for refs and pointers (because it's a class...) - this is not that much of an issue though
In Qt Creator, with the Clang code model selected (help -> about plugins -> C++), I see the following warning:
Semantic Issue | -Wold-style-cast |
---|---|
23:5: warning: use of old-style cast | |
enum.h:1089:21: note: expanded from macro 'BETTER_ENUM' | |
enum.h:679:23: note: expanded from macro 'BETTER_ENUMS_TYPE' | |
enum.h:500:13: note: expanded from macro 'BETTER_ENUMS_EAT_ASSIGN' | |
:0:0: note: (skipping 4 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) | |
enum.h:114:28: note: expanded from macro 'BETTER_ENUMS_ID' | |
enum.h:114:28: note: expanded from macro 'BETTER_ENUMS_ID' | |
enum.h:114:28: note: expanded from macro 'BETTER_ENUMS_ID' |
(If I have the default code model selected, I get warnings whenever I use BETTER_ENUM(), and code completion/coloring doesn't work for those enums, so I kinda have to use the clang code model)
Adding the following to _Iterable makes it somewhat more fully conform to what seems to be expected from a sequence/collection, when for example using with algorithms (just adding identical const versions of begin+end+iterator, plus adding value_type):
typedef const Element* const_iterator;
typedef Element value_type;
BETTER_ENUMS__CONSTEXPR const_iterator cbegin() const { return iterator(_array); }
BETTER_ENUMS__CONSTEXPR const_iterator cend() const { return iterator(_array + _size); }
Atleast it should be documented, preferably in the README.md
Declaring a Better Enum with a underlying type of bool
results in a C4800 compiler warning (tested with both MSVC 12.0 + MSVC 14.0, using /W3
).
BETTER_ENUM(Boolean, bool, TRUE = true, FALSE = false)
"example::Boolean::_enumerated": Variable wird auf booleschen Wert ("True" oder "False") gesetzt (Auswirkungen auf Leistungsverhalten möglich)
I think Better Enums should work the same as a built-in enum class
. The following source code does not raise a compiler warning:
enum class Boolean : bool {
TRUE = true,
FALSE = false
};
I know 5.4 is not officially supported but I still report for further development.
Please support it if possible. Compile errors on Linux 64-bit gcc 5.4.0
enum.h:577:1: error: ‘namespace’ definition is not allowed here
namespace better_enums {
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:247:15: error: local class ‘class main()::Word’ shall not have static data member ‘constexpr const size_t main()::Word::_size_constant’ [-fpermissive]
4, 3, 2, 1))
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:637:9: note: in expansion of macro ‘BETTER_ENUMS_ID’
BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT(VA_ARGS));
^
enum.h:243:5: note: in expansion of macro ‘BETTER_ENUMS_ID’
BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT_IMPL(VA_ARGS, 64, 63, 62, 61, 60,
^
enum.h:243:21: note: in expansion of macro ‘BETTER_ENUMS_PP_COUNT_IMPL’
BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT_IMPL(VA_ARGS, 64, 63, 62, 61, 60,
^
enum.h:637:25: note: in expansion of macro ‘BETTER_ENUMS_PP_COUNT’
BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT(VA_ARGS));
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:665:1: error: ‘namespace’ definition is not allowed here
namespace better_enums {
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:683:1: error: a function-definition is not allowed here before ‘{’ token
{
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:688:23: error: qualified-id in declaration before ‘(’ token
Enum::_from_value_loop(Enum::_integral value, std::size_t index)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:699:24: error: qualified-id in declaration before ‘(’ token
Enum::_from_string_loop(const char name, std::size_t index)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:710:31: error: qualified-id in declaration before ‘(’ token
Enum::from_string_nocase_loop(const char *name, std::size_t index)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:720:66: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline Enum::_integral Enum::_to_integral() const
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:726:31: error: qualified-id in declaration before ‘(’ token
Enum::_from_integral_unchecked(_integral value)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:732:29: error: qualified-id in declaration before ‘(’ token
Enum::_from_integral_nothrow(integral value)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:740:57: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline Enum Enum::_from_integral(_integral value)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:739:1: note: in expansion of macro ‘BETTER_ENUMS_IF_EXCEPTIONS’
BETTER_ENUMS_IF_EXCEPTIONS(
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:748:54: error: qualified-id in declaration before ‘(’ token
ToStringConstexpr inline const char Enum::_to_string() const
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:758:27: error: qualified-id in declaration before ‘(’ token
Enum::from_string_nothrow(const char *name)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:766:55: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline Enum Enum::_from_string(const char name)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:765:1: note: in expansion of macro ‘BETTER_ENUMS_IF_EXCEPTIONS’
BETTER_ENUMS_IF_EXCEPTIONS(
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:775:34: error: qualified-id in declaration before ‘(’ token
Enum::from_string_nocase_nothrow(const char *name)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:783:62: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline Enum Enum::from_string_nocase(const char *name)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:782:1: note: in expansion of macro ‘BETTER_ENUMS_IF_EXCEPTIONS’
BETTER_ENUMS_IF_EXCEPTIONS(
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:792:52: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline bool Enum::_is_valid(integral value)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:797:52: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline bool Enum::is_valid(const char *name)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:802:59: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline bool Enum::is_valid_nocase(const char *name)
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:807:55: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline const char Enum::name()
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:812:67: error: qualified-id in declaration before ‘(’ token
BETTER_ENUMS_CONSTEXPR inline Enum::_value_iterable Enum::_values()
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:817:59: error: qualified-id in declaration before ‘(’ token
ToStringConstexpr inline Enum::_name_iterable Enum::_names()
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:956:32: error: qualified-id in declaration before ‘(’ token
inline int Enum::initialize()
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:824:1: note: in expansion of macro ‘BETTER_ENUMS_DO_DEFINE_INITIALIZE’
DefineInitialize(Enum)
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
enum.h:1046:9: note: in expansion of macro ‘BETTER_ENUMS_DEFAULT_DEFINE_INITIALIZE’
BETTER_ENUMS_DEFAULT_DEFINE_INITIALIZE,
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:827:5: error: a function-definition is not allowed here before ‘{’ token
{ return a._to_integral() == b._to_integral(); }
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:829:5: error: a function-definition is not allowed here before ‘{’ token
{ return a._to_integral() != b._to_integral(); }
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:831:5: error: a function-definition is not allowed here before ‘{’ token
{ return a._to_integral() < b._to_integral(); }
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:833:5: error: a function-definition is not allowed here before ‘{’ token
{ return a._to_integral() <= b._to_integral(); }
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:835:5: error: a function-definition is not allowed here before ‘{’ token
{ return a._to_integral() > b._to_integral(); }
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
enum.h:837:5: error: a function-definition is not allowed here before ‘{’ token
{ return a._to_integral() >= b._to_integral(); }
^
enum.h:106:28: note: in definition of macro ‘BETTER_ENUMS_ID’
#define BETTER_ENUMS_ID(x) x
^
enum.h:1039:21: note: in expansion of macro ‘BETTER_ENUMS_TYPE’
BETTER_ENUMS_ID(BETTER_ENUMS_TYPE(
^
main.cpp:11:3: note: in expansion of macro ‘BETTER_ENUM’
BETTER_ENUM(Word, int, Hello, World)
^
Hi
Have you considered adding support for string_view for the _from_string methods?
It doesn't convert to char * so _names_match, _from_string and _from_string_loop would need to be overloaded.
BETTER_ENUM(Channel, char, Red = 1, Green, Blue)
int main() {
std::experimental::string_view s("Blue");
auto c = Channel::_from_string_nothrow(s);
std::cout << *c << std::endl;
return(0);
}
Hi, I have been trying to use Better Enums with CUDA, in a .cu file.
When supplying --expt-relaxed-constexpr
to NVCC, this works alright for the most part. However, when initializing the enum members, NVCC does something very strange.
Here is my testing code:
#include "enum.h"
BETTER_ENUM(Channel, char, Red = 1, Green, Blue);
int main() { }
Compile with nvcc constexpr-init.cu -std=c++11 -o constexpr-init
I get the following errors that are not understandable to me:
constexpr-init.cu:3:2362: error: lvalue required as left operand of assignment
BETTER_ENUM(Channel, char, Red = 1, Green, Blue);
^
constexpr-init.cu: In static member function 'static constexpr Channel::_optional Channel::_from_integral_nothrow(Channel::_integral)':
constexpr-init.cu:3:0: error: body of constexpr function 'static constexpr Channel::_optional Channel::_from_integral_nothrow(Channel::_integral)' not a return-statement
BETTER_ENUM(Channel, char, Red = 1, Green, Blue);
constexpr-init.cu: In static member function 'static constexpr Channel::_optional Channel::_from_string_nothrow(const char*)':
constexpr-init.cu:3:0: error: body of constexpr function 'static constexpr Channel::_optional Channel::_from_string_nothrow(const char*)' not a return-statement
constexpr-init.cu: In static member function 'static constexpr Channel::_optional Channel::_from_string_nocase_nothrow(const char*)':
constexpr-init.cu:3:0: error: body of constexpr function 'static constexpr Channel::_optional Channel::_from_string_nocase_nothrow(const char*)' not a return-statement
constexpr-init.cu: In static member function 'static constexpr Channel::_value_iterable Channel::_values()':
constexpr-init.cu:3:0: error: body of constexpr function 'static constexpr Channel::_value_iterable Channel::_values()' not a return-statement
NVCC creates .cpp files that are passed to the host compiler. These files can be inspected by adding the --keep
parameter to the command line.
If you do that and inspect the file, you will find the macro expansion somewhere, which looks like this:
namespace better_enums { namespace _data_Channel { }}class Channel { typedef better_enums::optional< Channel> _optional; typedef better_enums::optional< unsigned long> _optional_index; public: typedef char _integral; enum _enumerated: char { Red = 1, Green, Blue}; constexpr Channel(_enumerated value) : _value(value) { } constexpr operator _enumerated() const { return (_enumerated)(_value); } constexpr _integral _to_integral() const; static constexpr Channel _from_integral(_integral value); static constexpr Channel _from_integral_unchecked(_integral value); static constexpr _optional _from_integral_nothrow(_integral value); inline const char *_to_string() const; static constexpr Channel _from_string(const char * name); static constexpr _optional _from_string_nothrow(const char * name); static constexpr Channel _from_string_nocase(const char * name); static constexpr _optional _from_string_nocase_nothrow(const char * name); static constexpr bool _is_valid(_integral value); static constexpr bool _is_valid(const char * name); static constexpr bool _is_valid_nocase(const char * name); typedef better_enums::_Iterable< Channel> _value_iterable; typedef better_enums::_Iterable< const char *> _name_iterable; typedef better_enums::_Iterable< Channel> ::iterator _value_iterator; typedef better_enums::_Iterable< const char *> ::iterator _name_iterator; static constexpr const std::size_t _size_constant = (3); static constexpr std::size_t _size() { return _size_constant; } static constexpr const char *_name(); static constexpr _value_iterable _values(); static inline _name_iterable _names(); _integral _value; private: Channel() : _value((0)) { } constexpr explicit Channel(const _integral &value) : _value(value) { } static inline int initialize(); static constexpr _optional_index _from_value_loop(_integral value, std::size_t index = 0); static constexpr _optional_index _from_string_loop(const char * name, std::size_t index = 0); static constexpr _optional_index _from_string_nocase_loop(const char * name, std::size_t index = 0); friend struct better_enums::_initialize_at_program_start< Channel> ; }; namespace better_enums { namespace _data_Channel { static _initialize_at_program_start< Channel> _force_initialization; enum _PutNamesInThisScopeAlso { Red = 1, Green, Blue}; constexpr const Channel _value_array[] = {(Channel::Red = (1)), (Channel::Green), (Channel::Blue)}; constexpr const char *_the_raw_names[] = {("Red = 1"), ("Green"), ("Blue")}; constexpr const char *const *_raw_names() { return _the_raw_names; } inline char *_name_storage() { static char storage[] = "Red = 1,Green,Blue,"; return storage; } inline const char **_name_array() { static const char *value[Channel::_size_constant]; return value; } inline bool &_initialized() { static bool value = false; return value; } }}constexpr const Channel operator+(Channel::_enumerated enumerated) { return static_cast< Channel>(enumerated); } constexpr Channel::_optional_index Channel::_from_value_loop(_integral value, std::size_t index) { return ((index == _size()) ? _optional_index() : ((((((better_enums::_data_Channel::_value_array)[index])._value) == value) ? ((_optional_index)(index)) : (_from_value_loop(value, index + (1)))))); } constexpr Channel::_optional_index Channel::_from_string_loop(const char *name, std::size_t index) { return ((index == _size()) ? _optional_index() : ((::better_enums::_names_match(better_enums::_data_Channel::_raw_names()[index], name) ? ((_optional_index)(index)) : (_from_string_loop(name, index + (1)))))); } constexpr Channel::_optional_index Channel::_from_string_nocase_loop(const char *name, std::size_t index) { return ((index == _size()) ? _optional_index() : ((::better_enums::_names_match_nocase(better_enums::_data_Channel::_raw_names()[index], name) ? ((_optional_index)(index)) : (_from_string_nocase_loop(name, index + (1)))))); } constexpr Channel::_integral Channel::_to_integral() const { return (_integral)(_value); } constexpr Channel Channel::_from_integral_unchecked(_integral value) { return static_cast< _enumerated>(value); } constexpr Channel::_optional Channel::_from_integral_nothrow(_integral value) { return ::better_enums::_map_index< Channel> (better_enums::_data_Channel::_value_array, _from_value_loop(value)); } constexpr Channel Channel::_from_integral(_integral value) { return ::better_enums::_or_throw(_from_integral_nothrow(value), "Channel::_from_integral: invalid argument"); } inline const char *Channel::_to_string() const { return ::better_enums::_or_null(::better_enums::_map_index< const char *> (better_enums::_data_Channel::_name_array(), _from_value_loop(::better_enums::continue_with(initialize(), _value)))); } constexpr Channel::_optional Channel::_from_string_nothrow(const char *name) { return ::better_enums::_map_index< Channel> (better_enums::_data_Channel::_value_array, _from_string_loop(name)); } constexpr Channel Channel::_from_string(const char *name) { return ::better_enums::_or_throw(_from_string_nothrow(name), "Channel::_from_string: invalid argument"); } constexpr Channel::_optional Channel::_from_string_nocase_nothrow(const char *name) { return ::better_enums::_map_index< Channel> (better_enums::_data_Channel::_value_array, _from_string_nocase_loop(name)); } constexpr Channel Channel::_from_string_nocase(const char *name) { return ::better_enums::_or_throw(_from_string_nocase_nothrow(name), "Channel::_from_string_nocase: invalid argument"); } constexpr bool Channel::_is_valid(_integral value) { return _from_value_loop(value); } constexpr bool Channel::_is_valid(const char *name) { return _from_string_loop(name); } constexpr bool Channel::_is_valid_nocase(const char *name) { return _from_string_nocase_loop(name); } constexpr const char *Channel::_name() { return "Channel"; } constexpr Channel::_value_iterable Channel::_values() { return _value_iterable(better_enums::_data_Channel::_value_array, _size()); } inline Channel::_name_iterable Channel::_names() { return _name_iterable(better_enums::_data_Channel::_name_array(), ::better_enums::continue_with(initialize(), _size())); } inline int Channel::initialize() { if (better_enums::_data_Channel::_initialized()) { return 0; } ::better_enums::_trim_names(better_enums::_data_Channel::_raw_names(), better_enums::_data_Channel::_name_array(), better_enums::_data_Channel::_name_storage(), _size()); better_enums::_data_Channel::_initialized() = true; return 0; } constexpr bool operator==(const Channel &a, const Channel &b) { return (a._to_integral()) == (b._to_integral()); } constexpr bool operator!=(const Channel &a, const Channel &b) { return (a._to_integral()) != (b._to_integral()); } constexpr bool operator<(const Channel &a, const Channel &b) { return (a._to_integral()) < (b._to_integral()); } constexpr bool operator<=(const Channel &a, const Channel &b) { return (a._to_integral()) <= (b._to_integral()); } constexpr bool operator>(const Channel &a, const Channel &b) { return (a._to_integral()) > (b._to_integral()); } constexpr bool operator>=(const Channel &a, const Channel &b) { return (a._to_integral()) >= (b._to_integral()); }
(Sorry for the no spaces, but this is what I get out).
The error occurs at character 2362 which seems to be the expression Channel::Red = (1)
in the following statement:
constexpr const Channel _value_array[] = {(Channel::Red = (1)), (Channel::Green), (Channel::Blue)};
This is the end of my investigations, I don't know where to go from here.
I know that it's probably the NVCC that's broken, but can you think of a way to make this initialization work?
Better Enums testing currently uses CMake, Make, Python, and CxxTest. This makes it difficult to run tests on Windows without Cygwin installed.
It would be nice if running the tests didn't require Cygwin, or installing native ports of Make or Python, but only CMake and a C++ compiler. Better Enums would probably have to switch away from CxxTest to achieve this, or else commit files generated by CxxTest.
If not too difficult, it would be nice to also make it easy to develop without Cygwin, Make, or Python.
Consider this example:
BETTER_ENUM(TestEnum, int, One, Two);
int main()
{
TestEnum mine = TestEnum::One;
if (mine == TestEnum::One)
{
}
}
The comparison using ==
above fails on MSVC and GCC with the following:
main.cpp:1193:14: error: ambiguous overload for 'operator==' (operand types are 'TestEnum' and 'TestEnum::_enumerated')
if (mine == TestEnum::One)
~~~~~^~~~~~~~~~~
main.cpp:1193:14: note: candidate: operator==(TestEnum::_enumerated, TestEnum::_enumerated) <built-in>
main.cpp:1193:14: note: candidate: operator==(int, int) <built-in>
Since you're using an unscoped enumeration internally, a direct comparison cannot be performed because the compiler can cast _enumerated
either to int
or TestEnum
.
Yes, I realize I can use the +
operator but I find this to be unintuitive semantically and unnecessary.
As a test I added an additional operator overload:
BETTER_ENUMS_CONSTEXPR_ inline bool operator ==(const Enum &a, const Enum::_enumerated &b) \
{ return a._to_integral() == b; } \
This fixed the compiler error. I feel that the Enum class should explicitly handle operations on its enumerated type. I also find it odd that there is no way to grab the internal enumerated type that a Enum variable represents, much like Enum::_to_integer()
but instead something like Enum::_to_enum()
, which has a return type of Enum::_enumerated
.
Thoughts? I'm happy to contribute a PR for this if you feel that this is a good addition.
In issue #24 this has been already discussed.
If Better Enums would provide a CMake Package, it would be much easier to use by clients.
Don't get me wrong: Copy & Pasting one header file isn't hard, but it is against the Don't Repeat Yourself Principle (DRYP). Currently I implemented a custom (old-style) FindBetterEnums.cmake
CMake Find Module, but that is also a poor man's solution.
I think it would be much cleaner to provide a CMake package together with a proper CMake Installation.
I could provide a clean and modern CMakeLists.txt
file for the project via a PR if it is desired (I have a bit of experience with CMake). @rcdailey already provided one, but it haven't been included in the master
?
If this issue isn't rejected I would install Better Enum in a way, that #includes
would look like the following (see #11):
#include <better_enum/enum.h>
#include <better_enum/n4428.h>
IMO that would be a little clearer.
Now that we have ci, you might want to add the travis status image and the appveyor status badge. This way users can immediately see that you project has automated tests and feel a little more comfortable in using it. And potential contributors can be confident that their contributions are automatically tested.
I am migrating from a 'smart enum' library based on what used to be 'BOOST_ENUM'. I noticed that the _from_string function is static, and recall having been bitten by this before in that library.
My problem is that it is possible to call this as a member function on an existing enumeration with rather unpleasant consequences.
BETTER_ENUM(Enum, int, A, B, C)
Enum X = Enum::_from_string("A")
X._from_string("B"); // legal, but no change of X
I would much prefer the function to be non-static, in order to make sure that at the end X == Enum::B
holds, and (for example) having a factory function akin to what is present in the standard library, such as:
X = make_enum<Enum>("A");
X = make_enum<Enum>(2);
Also, I am missing constructors that allow for example:
Enum X("A")
Is there a good reason to not allow this?
Copy pasting the SafeSwitch example to project created via Visual Studio 2017 and compiling the project, no warning is being produced by the compiler.
Compiler Version:
# From Developer Command Prompt for VS 2017
>cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25019
Sample project:
BetterEnumTest.zip
A macro named ENUM
is a prime candidate for easy symbol ambiguity. And in my code base, I am seeing this. This requires me to do silly inclusion trickery like so:
// All normal includes at the top
#undef ENUM
#include <enum.h> // This one must be last in the list of include directives
ENUM(Fubar, int, One, Two, Three);
The best solution to this would be to make a more generic name for your macros. Boost does this by prefixing macros consistently. This makes them a bit more verbose, but it's a necessary annoyance when dealing with macros. I think maybe a name like BETTER_ENUM()
would work just fine.
The include is odd as well. I am free to nest your header file in any directory I want, so I can resolve the issue that way. But "out of the box" your solution implies you include enum.h
directly. Instead, I think you should nest your includes to avoid ambiguous includes:
#include <better/enum.h>
This might require restructuring your repository a bit, to make it obvious to people that the include is supposed to be done in a relative way like I've shown above.
Hi,
I've been playing around with better enums recently and I was trying to compare between the enum class and its enumerated values:
auto a = Hello::World;
if (a == Hello::World)
...
As stated in some other issues, this triggers a compilation error.
I dug inside the enum.h file and foudn a way to patch macro definitions to include support for such comparison cases.
However, I'm not entirely sure this patch fits well with the rest of the library, I've been testing it against my own use cases and it seems to do the job for me.
Are you interested in a patch proposal ? If so, should I create a branch directly on your repository or do you prefer a diff file attached to this ticket ?
Regards.
Normal scoped enumerations are default constructible:
enum class Fubar { One, Two, Three };
auto myinstance = Fubar{};
However, your ENUM
macro does not yield a struct that matches these semantics:
ENUM(Fubar, int, One, Two, Three);
auto myinstance = Fubar{}; // compiler error on MSVC12
The error I get is:
error C2248: 'Fubar' : cannot access private member declared in class 'Fubar'
These semantics are extremely important for template metaprogramming. Example test case:
template<typename T>
void DeserializeEnum(T& enumerator, T const& default_value = T{});
Internally, the method above will attempt to stream in a string and map it to a proper enumerator. If the string does not map to a valid enumerator, it will instead set it to the value of default_value
. This template works just fine for true scoped enumerations, but not with your struct declared by the ENUM
macro.
See #26.
I tried the to expand the item limit to fit my needs.
I have an enum with ~1400 list members. Using MSVC I triggered C1112 (compiler limit:'1400' too many macro arguments, only '127' allowed.
You can do something like this:
// Make sure it's possible to compare string correctly
constexpr bool static_strequal_helper(const char * a, const char * b, unsigned len) {
return (len == 0) ? true : ((*a == *b) ? static_strequal_helper(a + 1, b + 1, len - 1) : false);
}
// This is the base class using CRTP for capturing the enum' class type
// With the macro trick below, it allow auto registration even in classes of your enum, and
// also O(1) runtime string lookup-to-value matching
template <typename T>
struct CatalogEnum
{
typedef T ClassType;
static std::map<const char *, int> enumToValue;
static void registerField(const char * name, int value) { enumToValue[name] = value; }
static int fromStringImpl(const char * name) { return enumToValue[name]; }
};
/* This is the basic block. Because your enum derives from CatalogEnum, it has a registerField member, so it can be registered at run time (before any usage)
Also, I've provided a basic constexpr trick to match against an enum value, but not the code
for complete lookup.
It should not be difficult to iterate over all the RegisterEnum entries at compile time to perform O(N) lookup for a fromString method.
Please notice that even if you enum as same value for 2 field, it'll generate two different instance of this
template, and will still work correctly, since the pointer to _get_field_name is different */
template <typename Class, int value, typename const char* (*enumName)()>
struct RegisterEnum
{
// Compile-time version
template<size_t N>
constexpr bool match_string(const char (&a)[N]) const { return static_strequal_helper(a, (*enumName)(), N); }
// Run-time version
bool match_string(const char* a) const { return strcmp(a, (*enumName)()) == 0; }
RegisterEnum() { CatalogEnum<Class>::registerField((*enumName)(), value); }
};
// Basic macro trick, it declares a static function, and uses its address as a templated differentiator
// Then the templated object constructor takes care of calling the Catalog::registerField method to register
// the enum field
#define DeclMember(A) \
static const char *__get##A() { return #A; } \
RegisterEnum<ClassType, A, &__get##A> __##A;
// And some of your code:
#define ENUM(Name, type, A, B, C) \
struct Name : CatalogEnum<Name> { enum Type : type { A, B, C }; \
DeclMember(A) \
DeclMember(B) \
DeclMember(C) \
static Type fromString(const char * name) { return (Type)fromStringImpl(name); } \
template <size_t N>
static constexpr fromString(const char (&a)[N]); // Should iterate through all RegisterEnum and call match_string(a) on them
[your actual code here]
};
// Usage:
struct A
{
ENUM(Color, int, Blue, Red, Green); // OMG, it works!
void exampleMethod() { Color::Type c = Color::fromString("RED"); }
};
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.