Using elements of a constant array as cases in a switch statement - c++

I'm attempting to map a set of key presses to a set of commands. Because I process the commands from several places, I'd like to set up a layer of abstraction between the keys and the commands so that if I change the underlying key mappings, I don't have to change very much code. My current attempt looks like this:
// input.h
enum LOGICAL_KEYS {
DO_SOMETHING_KEY,
DO_SOMETHING_ELSE_KEY,
...
countof_LOGICAL_KEYS
};
static const SDLKey LogicalMappings[countof_LOGICAL_KEYS] = {
SDLK_RETURN, // Do Something
SDLK_ESCAPE, // Do Something Else
...
};
// some_other_file.cpp
...
switch( event.key.keysym.key ) {
case LogicalMappings[ DO_SOMETHING_KEY ]:
doSomething();
break;
case LogicalMappings[ DO_SOMETHING_ELSE_KEY ]:
doSomethingElse();
break;
...
}
When I try to compile this (gcc 4.3.2) I get the error message:
error: 'LogicalMappings' cannot appear in a constant-expression
I don't see why the compiler has a problem with this. I understand why you're not allowed to have variables in a case statement, but I was under the impression that you could use constants, as they could be evaluated at compile-time. Do constant arrays not work with switch statements? If so, I suppose I could just replace the array with something like:
static const SDLKey LOGICAL_MAPPING_DO_SOMETHING = SDLK_RETURN;
static const SDLKey LOGICAL_MAPPING_DO_SOMETHING_ELSE = SDLK_ESCAPE;
...
But that seems much less elegant. Does anybody know why you can't use a constant array here?
EDIT: I've seen the bit of the C++ standard that claims that, "An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5)...". I still don't see why a constant array doesn't count as an "enumeration type initialized with a constant expression." It could just be that the answer to my question is "because that's the way that it is," and I'll have to work around it. But if that's the case, it's sort of disappointing, because the compiler really could determine those values at compile-time.

Referring to sections of the C++ standard: 6.4.2 requires that case expressions evaluate to an integral or enumeration constant. 5.19 defines what that is:
An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type template parameters of integral or enumeration types, and sizeof expressions. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumeration types can be used. In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.
So if your question was "why does the compiler reject this", one answer is "because the standard says so".

Array references aren't "constant enough", regardless.
You just need to do the mapping slightly differently. You want the same action to occur when the logical key is pressed, so use the logical key codes in the case clauses of the switch statement. Then map the actual key code to the logical code, possibly in the switch itself, or possibly before-hand. You can still use the LogicalMappings array, or a similar construct. And, as an aid to G11N (globalization), you can even make the mapping array non-constant so that different people can remap the keys to suit their needs.

I'll go on a limb here since nobody else replied to this and I've been mostly doing Java recently, not C++, but as far as I seem to recall an array lookup is not considered a constant integer even if the result of the lookup can be determined at compile time. This may even be an issue in the syntax.

Is there a comparison operator defined for the "LogicalMappings"? If not then that is the error.

There is a library called signal in boost that will help you create a event mapping abstraction.If you have time this should be better approach

you could also use an array of function pointers or functors (I suppose functor addresses), to avoid the switch statement altogether & just go from array index -> function pointer / functors directly.
for example (warning, untested code follows)
class Event // you probably have this defined already
{
}
class EventHandler // abstract base class
{
public:
virtual void operator()(Event& e) = 0;
};
class EventHandler1
{
virtual void operator()(Event& e){
// do something here
}
};
class EventHandler2
{
virtual void operator()(Event& e){
// do something here
}
};
EventHandler1 ev1;
EventHandler2 ev2;
EventHandler *LogicalMappings[countof_LOGICAL_KEYS] = {
&ev1,
&ev2,
// more here...
};
// time to use code:
Event event;
if (event.key.keysym.key < countof_LOGICAL_KEYS)
{
EventHandler *p = LogicalMappings[event.key.keysym.key];
if (p != NULL)
(*p)(event);
}

A compiler guru at work explained this to me. The problem is that the array itself is constant, but indices to it aren't necessarily const. Thus, the expression LogicalMappings[some_variable] couldn't be evaluated at compile-time, so the array winds up being stored in memory anyway rather than getting compiled out. There's still no reason why the compiler couldn't statically evaluate array references with a const or literal index, so what I want to do should theoretically be possible, but it's a bit trickier than I'd thought, so I can understand why gcc doesn't do it.

Related

Branch with if constexpr() on constinit variables? [duplicate]

I ran across this seemingly odd behavior. A const at top level can be used for array size declarations, but not when the const is in a class.
Here's a compiler explorer demo for MSVC, CLANG and GCC all producing an error:
expression did not evaluate to a constant
It's not really a constant if it's not const at the top level?
There's some argument to be made because top level constants can often be stored in read-only memory, while constants that are not at top level cannot. But is this behavior correct?
struct A {
const int i{ 3 };
};
int main()
{
const int ii{ 3 };
A a;
int j[a.i]{}; // C2131: expression did not evaluate to a constant
int k[ii]{};
}
Generally, you can use (meaning perform an lvalue-to-rvalue conversion on) objects with lifetime starting outside a constant expression only if they are variables marked constexpr or their subobjects (plus some other special cases, that I don't think are important here, see [expr.const]/4 for details).
That you can use a const int variable at all in a constant expression is already a very specific exception. Essentially const-qualified integral and enumeration type variables are also usable in constant expressions if you could have added constexpr to them (meaning that their initializer expression is a constant expression).
This exception is there I guess purely for historical reasons, since it had been allowed before constexpr was introduced in C++11.
Note that all of this talks about variables and their subobjects. Non-static data members are specifically not variables and the exception doesn't apply to them. With constexpr this is more obvious by not allowing it on the declaration of a non-static data member in the first place.
The historical rule was never extended to encompass other types that could be marked constexpr, so e.g. const A a; will not help although that would actually cause a to be storable in read-only memory the same way a const int would.
If an object is none of the cases mentioned above, then an lvalue-to-rvalue conversion on it in a constant expression is not allowed, since it is assumed that the value of the object is not determined at compile-time.
Now, in theory the compiler could still do some constant folding and determine that even other objects' values are definitively known at compile-time. But I think the intention is that whether or not an expression is a constant expression should be (reasonably) well-defined independently of the implementation and so shouldn't rely on how much analysis the compiler can do.
For example
A a;
A b(a);
is also guaranteed to result in b.i == 3. How far do you want to require a compiler to go back or keep track of evaluations? You would need to make some definitive specification if you want to keep the behavior consistent between compilers. But there is already a simple method to indicate that you want the compiler to keep track of the values. You just have to add constexpr:
constexpr A a;
constexpr A b(a);
Now b.i can be used as array index (whether or not it is const and whether or not it is initialized).
With the current rules, any compiler only needs to evaluate the value of objects at compile-time when it sees a constexpr variable or a const integral/enumeration type variable. For all other variables it doesn't need to keep track of values or backtrack when it sees them used in a context which requires a constant expression.
The additional effect of constexpr implying const on the variable makes sure that its value will also never be changed in a valid program and so the compiler doesn't need worry about updating or invalidating the value after the initial computation either. And whether or not an expression is a constant expression is (mostly) implementation-dependent.
ii is a compile-time constant. Its value is known at compile-time, and cannot be changed at runtime. So, ii can be used for fixed array sizes at compile-time.
A::i is not a compile-time constant. It is a non-static instance member. Its value is not known until runtime. After an A object is constructed and its i member is initialized, the value of i cannot be changed because of the const, but the caller can initialize i with whatever value it wants, eg: A a{123};, and thus different A objects can have different i values. So, i cannot be used for fixed array sizes at compile-time. But, it can be used for dynamic array sizes at runtime, via new[], std::vector, etc.
TL;DR
Your assumption that const always implies compile time constant is incorrect. See examples at the end of this answer for more details on this.
Now the problem in using a.i as the size of an array is that in standard C++, the size of an array must be a compile time constant, but since i is a non-static data member, it requires an object to be used on. In other words, after construction of the class object nonstatic data member i gets initialized, which in turn means that a.i is not a constant expression, hence we get the mentioned error saying:
expression did not evaluate to a constant
To solve this, you can make i be a constexpr static data member, as shown below. This works because using a static data member doesn't require an object instance (and hence no this pointer).
struct A {
constexpr static int i{ 3 };
};
int main()
{
const int ii{ 3 };
A a;
int j[a.i]{}; //Correct now and works in all compilers
int k[ii]{};
}
I just don't get why a regular const works in some places but not others.
Perhaps you assuming that const implies compile time constant which is a wrong assumption. An example might help you understand this better:
int i = 10; //i is not a constant expression
const int size = i; //size is not a constant expression as the initializer is not a constant expression
//------vvvv------>error here as expected since size is not a constant expression
int arr[size]{};
On the other hand if you were to make i const as shown below, the program will work fine.
const int i = 10; //note the const added here so that now i is a constant expression
const int size = i; //size is a constant expression as the initializer is a constant expression
//------vvvv------>NO ERROR HERE as expected since size is a constant expression
int arr[size]{};

Are there benefits to using a constexpr variable that is never intended to be used in a constant expression?

This is a follow up question to this one.
From one of the answers in the question linked, it makes sense that the purpose of declaring a variable as constexpr is not truly to inform the compiler of the possibility of using that variable as a constant expression (although this is the end behavior of the compiler) but rather to convey to other programmers the intent that the variable can be used in a constant expression. My question is whether there is any benefit, performance or otherwise, to declaring a variable as constexpr if the variable is not intended to be used in a constant expression (e.g. a constant member variable of a class which only appears in non-constant expressions with other non-const member variables). Also assume that this is not for an embedded system.
I work with code where the previous developers seem to use constexpr like it's candy even when it's not clear that the intent is for the variable to be used in a constant expression (like in the example from the previous paragraph). I want to know if they knew something that I don't.
but rather to convey to other programmers the intent that the variable can be used in a constant expression.
This is a matter of opinion but I beg to differ.
In principle, I think it is better to make everything that can be known and computed at compile time a constexpr. The fact that it can also be used in constant expressions is a side benefit. Use of constexpr may have performance related benefits too.
The key point I want to make is that "the value is known at compile time" is a characteristic of the variable/data. It is better to capture that characteristic by using a language provided feature. The only time one could justify not using it will be if there are any adverse side effects.
Yes, constexpr variables are useful for more than just conveying intent and using in other constant expressions.
If you mark a variable constexpr, the compiler will enforce that it itself is initialized with a constant expression. You can use this to make sure that the things you expect to be evaluated at compile time actually are. For example:
constexpr int constexpr_function() { return 123; }
int expensive_runtime_function() { return 123; }
void f()
{
constexpr int good1 = constexpr_function(); // OK.
// constexpr int good2 = expensive_runtime_function(); // Good: error.
const int bad1 = constexpr_function(); // OK.
const int bad2 = expensive_runtime_function(); // Bad: unexpected runtime cost.
}

how to use boolean expression in switch in C++

I need to convert the if else into switch statement, please help:
if (XMLString::compareString(xmlch_Title, XMLString::transcode("abc")) == 0 ) {
out_Config.abc = XMLString::transcode(xmlch_Value);
} else if (XMLString::compareString(xmlch_Title, XMLString::transcode("def")) == 0 ) {
out_Config.def = XMLString::transcode(xmlch_Value);
} .......
Note: Using a boolean expression as the condition of a switch would, in your case, be of little use since you then could only have two possible outcomes of the switch-statement:
Going to "case true: ", or "case false: "
Introduction
As stated in the Standard (n3797), the condition used with a switch must be implicitly convertiable to either integral, or enumeration, type.
6.4.2p2 The switch statement [stmt.switch]
The condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (Clause 4) to an integral or enumeration type**. Integral promotions are performed. Any statement within the switch statement can be labeled with one or more case labels as follows:
case constant-expression:
where the constant-expression shall be a converted constant expression (5.19) of the promoted type of the switch condition. No two of the case constans in the same switch shall have the same value after conversion to the promoted type of the switch condition.
What this means is that the theoretical implementation below is ill-formed, since std::string can't implicitly be converted to an integral or enumeration type (1), and neither is it usable in a constant-expression (2).
std::string get_string ();
switch (get_string ()) { // (1)
case std::string ("stack"): ...; // <----.
case std::string ( "over"): ...; // <----|-- (2)
case std::string ( "flow"): ...; // <----'
}
Am I completely out of luck?
Not really, if you were to use a hashing function to generate a hash for the strings involved, you can theoretically use a switch; just be careful about potential collisions of said hashes.
Also note that there must be an implementation usable in constant-expressions for the case-labels.
Is it worth writing such complex code to solve this kind of problem? Probably not.
if you want to factor out the common part, you'll probably need to use an array of struct containing the transcoded string constant and a lambda doing the then operation. Something along the lines of:
#include <functional>
struct check_case {
const XMLString code;
const std::function <void (Config &, const XMLString &)> handler;
};
check_case check_list[2] = {
{XMLString::transcode("abc"), [](Config &out_Config, const XMLString &code) { out_Config.abc = code;}},
{XMLString::transcode("def"), [](Config &out_Config, const XMLString &code) { out_Config.def = code;}},
};
for (auto c : check_list) {
if (XMLString::compareString(xmlch_Title,c.code) == 0){
c.handler(out_Config, XMLString::transcode(xmlch_Value));
break;
}
}
It's still quite verbose though, and I'm not sure you'd find it more easily readable. Besides, you don't get to cascade your handlers as you could with a normal switch (which was perhaps the original reason for your question).
Alternatively, you could embed all that in a dedicated class, where it's easier to build a context (the class instance member fields). There it would be easier to cascade methods manually. As above, the readability isn't quite up to par either.
Look at it from a different point of view. Do it in two stages...
Stage 1: Extract the text in the quotes "abc" or "def"
Stage 2: switch (based on variable from stage 1) and case will provide implementation

Is there a reason to use enum to define a single constant in C++ code?

The typical way to define an integer constant to use inside a function is:
const int NumbeOfElements = 10;
the same for using within a class:
class Class {
...
static const int NumberOfElements = 10;
};
It can then be used as a fixed-size array bound which means it is known at compile time.
Long ago compilers didn't support the latter syntax and that's why enums were used:
enum NumberOfElementsEnum { NumberOfElements = 10; }
Now with almost every widely used compiler supporting both the in-function const int and the in-class static const int syntax is there any reason to use the enum for this purpose?
The reason is mainly brevity. First of all, an enum can be anonymous:
class foo {
enum { bar = 1 };
};
This effectively introduces bar as an integral constant. Note that the above is shorter than static const int.
Also, no-one could possibly write &bar if it's an enum member. If you do this:
class foo {
static const int bar = 1;
}
and then the client of your class does this:
printf("%p", &foo::bar);
then he will get a compile-time linker error that foo::bar is not defined (because, well, as an lvalue, it's not). In practice, with the Standard as it currently stands, anywhere bar is used where an integral constant expression is not required (i.e. where it is merely allowed), it requires an out-of-class definition of foo::bar. The places where such an expression is required are: enum initializers, case labels, array size in types (excepting new[]), and template arguments of integral types. Thus, using bar anywhere else technically requires a definition. See C++ Core Language Active Issue 712 for more info - there are no proposed resolutions as of yet.
In practice, most compilers these days are more lenient about this, and will let you get away with most "common sense" uses of static const int variables without requiring a definition. However, the corner cases may differ, however, so many consider it to be better to just use anonymous enum, for which everything is crystal clear, and there's no ambiguity at all.
Defining static constants directly in the class definition is a later addition to C++ and many still stick to the older workaround of using an enum for that. There might even be a few older compilers still in use which don't support static constants directly defined in class definitions.
Use of enum have one advantage. An enum type is a type, so if you define, for example:
enum EnumType { EnumValue1 = 10, EnumValue2 = 20 ... };
and you have a function like:
void Function1(EnumType Value)
the compiler checks that you are passing a member of the enum EnumType to the function, so only valid values for parameter Value would be EnumValue1 and EnumValue2. If you use constants and change the function to
void Function1(int Value)
the compiler checks that you are passing an int (any int, a constant, variable or literal) to the function.
Enum types are good for grouping related const-values. For only one const value, I do not see any advantage.
In your case, I'd use a constant as well. However, there are other cases where I might be adding other, related constants. Like this:
const int TextFile = 1; // XXX Maybe add other constants for binary files etc.?
In such cases, I use an enum right away with a single value, like this:
enum FileType {
TextFile = 1
// XXX Maybe add other values for binary files etc.?
}
The reason is that the compiler can then issue warnings when I'm using the constant value in switch expressions, as in:
FileType type;
// ...
switch ( type ) {
case TextFile:
// ...
}
In case I decide to add another constant value which is related to the existing value (a different type of file, in this example), virtually all compilers will issue a warning since the new value is not handled in the switch statement.
If I had used 'int' and constants instead, the compiler wouldn't have a chance to issue warnings.
The only reason for using the "enum hack" is that old compilers do not support in-class const definitions, as you say in your question. So, unless you suspect that your code will be ported to an old compiler, you should use const where const is due.
I think that there's no reason to use an enum, and that it's actually better to use a static const int for this purpose, since an enum has its own type (even if implicitly convertible to an integer).
There is a difference between those two. Enums don't have an adress as far as I know. static const ints do though. So if someone takes the adress of the const static int, casts away the const, he can modify the value (although the compiler might ignore the change because he thinks it's const). This is of course pure evil and you should not do it - but the compiler can't prevent it. This can't happen with enums.
And of course - if you (for some reason) need the adress of that const, you need the static const int.
In short - enum is an rvalue while const static int is an lvalue. See http://www.embedded.com/story/OEG20011129S0065 for more details.
Well, the portability is a good reason for using enum. It is great, because you don't have to worry whether your compiler supports "static const int S = 10" or not...
Also, as far as I remember, static variable must be defined somewhere, as well as declared, and the enum value must be declared only.
bottom line - use const.
more details:
I'm not a c++ expert, but this seems a more general design question.
If it's not a must, and you think there's a very low/non-existent probability that the enum will grow to have more then one value, then use a regular const.
even if you are wrong and at some point in the future there will be more values, making an enum the right choice - a simple refactoring and you change you const to enum.

How does multiplication work for C++ enums?

We have a template conversion function intended for use with numeric datatypes. Inside it contains a construct that makes it not compile with types like pointers.
template<class To, class From>
To ConvertTo( From what )
{
assert( 2 * what == what * 2 ); // this will not compile for pointers
//skipped
}
This function compiles and works allright when a enum is passed as the second template parameter:
enum TSomeEnum {
SE_First,
SE_Second
};
TSomeEnum enumValue = SE_First;
int result = ConvertTo<int>( enumValue );
The above code compiles and runs as intended on VC++7.
How does the operation * work for enums? Is it a way to undefined behaviour?
Enums degrade to ints (old C feature) so that why this is working. I don't think it's undefined behaviour although it might be the sort of behaviour you don't want/expect.
Enums are promoted to integers for arithmetic operations.
The values of enums are always numbered up from zero (this is part of the standard), but you can specify whatever values you like. Automatic numbering then procedes incrementally from the last item you explicitly numbered.
The compiler uses the smallest integral type that contains all of the values of an enum to store it.
eg:
enum foo
{
VALUE0, /* = 0 */
VALUE1, /* = 1 */
VALUE3 = 1234,
VALUE4 /* = 1235 */
};
I think peterchen has the ideal answer, but if boost is too much for you, change your current version to:
template<class To, class From>
To ConvertTo( From what, From checkConvertableToInt = 2 )
{
}
C++ doesn't allow implicit conversions from integers to enumerations. Similarly, it's only possible to implicitly convert the null pointer constant (0) to a pointer type.
Some of the other answers refer to an enumeration being implemented as an int or being promoted to an int. This is not true. In C++, an enumeration is defined to have an underlying type that is capable of storing all values in the enumeration. The following enumeration will therefore have an underlying type of 'unsigned int' or a larger signed/unsigned integral type:
enum E {
E0
, E1=MAX_INT+1u
};
The enumeration then follows the rules for its underlying type when you use it in an operation.
Timo described why it works.
For a safer test, you could use boost::TypeTraits
Richard Corden's answer gives a good mechanism to force enums to fail in the assert, however the side effect can cause problems, as you cannot be sure the code inside the assert will always run.
This test should probably by placed in a generic function to avoid the side effects.
template<class To, class From>
To ConvertTo( From what )
{
assert( testIntegralType(what) );
}
template<class T>
void testIntegralType( T val )
{
T v1 = val;
assert( 2*v1 == 2*val );
}
enums are stored as integers, and can even be cast to integers.
The compiler probably stored your above enum as
enum TSomeEnum {
SE_First = 0,
SE_SEcond = 1
};
I'm not sure if this numbering system is part of the C/C++ standard or just how compilers do it, but I've always seen them in order.
Anyway, to answer your question, since they're stored as ints underneath, it should be able to be multiplied. Some compilers may give you a warning about type conversion though.