1

I would like to use a (const) struct member as a selector in switch/case statement. The problem is that I get a "case expression not constant" or "illegal constant expression". Here is a brief example

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct _myStruct
{
    unsigned int value_;
    unsigned int index_;
} myStruct;

#define VALUE_0 0
const unsigned int VALUE_1 = 1;
const myStruct VALUE_2 = {2, 0};

int main()
{
  srand((unsigned int)time(0));

  switch(rand()%4)
  {
    case VALUE_0:
      printf("Value is 0\n");
      break;
    case VALUE_1:
      printf("Value is 1\n");
      break;
    case VALUE_2.value_:
      printf("Value is 2\n");
      break;
    case 3:
      printf("Value is 3\n");
      break;
  }

  return 0;
}

The case VALUE_2.value_ option generates the compiler error. BTW, VALUE_2 is indeed a constant.

I'm working with ANSI C. C++ gives the same error, anyway.

Any hints?

il_mix
  • 553
  • 8
  • 20
  • 1
    your case label does not reducing to an `integer constant` – vinay hunachyal Jul 18 '14 at 10:32
  • 1
    If you change `const myStruct VALUE_2` to `constexpr myStruct VALUE_2` (in C++11) to have compile time constant, it compiles. `const` doesn't mean it is known at compile time. – Jarod42 Jul 18 '14 at 10:35
  • Possible duplicate of: http://stackoverflow.com/questions/14069737/switch-case-error-case-label-does-not-reduce-to-an-integer-constant There is `const int` in both questions used in as a label in a `switch` statement. – Dmitry Jul 18 '14 at 10:37
  • 1
    Because it is not a constant. Keyword `const` makes a variable 'non-writable' but not a constant. – CiaPan Jul 18 '14 at 10:43
  • @Jarod42 this is not C++11 though – Drew McGowen Jul 18 '14 at 12:31
  • Sorry, I've already given an answer, and hadn't thought about the rather vague term “ANSI C”: Some people refer to C89/C90 by ANSI C. Are you interested in these standards? If yes, think about tagging with C89 (and I'll reword my answer), here on SO, C99 usually is considered the “default” C standard. – mafso Jul 18 '14 at 15:17
  • With "ANSI C" I meant "plain C", so not C++ (since I've already read about `constexpr` somewhere else). Didn't thought about the various C versions... – il_mix Jul 21 '14 at 07:56

3 Answers3

2

In C, a const-qualified variable means "read only object in memory", but it's not considered an integer constant expression.

An integer constant expression is, roughly speaking, an expression involving only literal integer constants as operands.

In particular, a #define-d constant which expand to an integer literal is a valid constant here.

The case labels require integer constant expressions, and not variables. The const qualifier is not making any difference, and this kind of objects are not allowed.

pablo1977
  • 4,281
  • 1
  • 15
  • 41
  • Thanks for the answer. But what's the difference between `VALUE_1` and `VALUE_2` from the compiler point of view? They are both read-only objects, not preprocessor constants (read `#define`). – il_mix Jul 18 '14 at 12:15
  • I think it depends on the compiler, or well the compiler options. I have compiled your code with GCC 4.7 under strict standard C99 rules, and an error message is raised on VALUE1, because is not an **integer constant expression**. It's not portable code. – pablo1977 Jul 18 '14 at 12:23
1

In short, const doesn't mean “constant expression” (the const keyword is a little confusing IMO). Precisely,

C11 6.8.4.2 p.2 (emph. mine)

The expression of each case label shall be an integer constant expression […]

So, the question is, what an integer constant expression is; C11 addresses this in 6.6:

Description (p. 2)

A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.

The Constraints section (p. 3/4) goes on:

Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.115)

Each constant expression shall evaluate to a constant that is in the range of representable values for its type.

And the footnote:

115) The operand of a sizeof or _Alignof operator is usually not evaluated (6.5.3.4).

Semantics (p. 5/6)

An expression that evaluates to a constant is required in several contexts. […] An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof or _Alignof operator. [emph. mine, omitted footnotes 116 and 117]

The emphasized list of allowed operands doesn't contain variables (at least not in general, sizeof VALUE_2 would be OK), no matter if they are const-qualified or not (you mentioned C++ in your question; if you're interested, have a look at C++11's constexpr).

And (ibid. p. 10)

An implementation may accept other forms of constant expressions.

I quoted C11; C99 is basically the same (but doesn't have the _Alignof operator), with the same sections.

HTH

mafso
  • 5,433
  • 2
  • 19
  • 40
  • Thanks for the detailed answer. I will read it carefully some more times to better fix the concepts in my head. – il_mix Jul 21 '14 at 07:53
0

If you make the const member static, it will satisfy the requirements for switch.

struct my_struct
{
    static const int value_a = 1;
    static const int value_b = 2;

    void handle_value(int value)
    {
        switch(value)
        {
            case my_struct::value_a:
            {
                // Do something fancy
                break;
            }
            case my_struct::value_b:
            {
                // Do something fancy
                break;
            }
            default:
            {
                // Do something fancy
                break;
            }
        }
    }
};
pcdangio
  • 294
  • 2
  • 13