12

Some code.cpp file contains

extern const int v1;
extern const int v2;
extern const int v3;
extern const int v4;

int _tmain(int argc, _TCHAR* argv[])
{
    int aee = v1;
    switch (aee)
    {
    case v1:
        break;
    case v2:
        break;
    case v3:
        break;
    case v4:
        break;
    }
        return
}

Another file definition.cpp contains

const int v1 = 1;
const int v2 = 2;
const int v3 = 3;
const int v4 = 4;

When I do compile I got error C2051: case expression not constant However when I remove extern everything is just fine.

Is there any way to make it work with extern?

Unicorn
  • 1,397
  • 1
  • 15
  • 24
  • what are you trying to achieve with this? – Nim Apr 07 '11 at 09:50
  • Are you able to compile without extern? Isn't it throwing errors for redefinition? – mukeshkumar Apr 07 '11 at 09:53
  • Actually this is used in a more complex way deep in the framework. My goal is to avoid recompilation of some major portion code when changing some header files – Unicorn Apr 07 '11 at 09:55
  • Hmm, smells a bit fishy!! If you need to take this kind of approach, there is bound to be a better way of doing it (maps, if-else, inheritance - whatever, not such a low-level hack) - my 2c. – Nim Apr 07 '11 at 10:07
  • 1
    @hype: constants have internal linkage by default, so you won't get "multiple definition" errors if you remove `extern` here. They will all have the value zero, though. – Mike Seymour Apr 07 '11 at 10:26
  • @Nim: Maybe.. But there are other limitations too long and difficult to explain. Actually this way our library handles errorCodes defined across. This abosletely Ok for us but we it a pity we cant use errorCodes in the switch statement.. :( – Unicorn Apr 07 '11 at 10:47
  • #defines work well for this sort of thing. Just make the names of them nice and long, sue to namespace issues. – Tom Andersen Jul 05 '12 at 19:16

2 Answers2

9

No. switch only works with fully defined integral type constants (including enum members and classes that unambiguously cast to integral type). here is a link to an old reference of MSDN, but what is says is still valid.

This link that I provided in a comment to another answer explains what optimizations compilers may perform to assembly code. If this was delayed to the linking step, it would not be easily possible.

You should therefore use if..else if in your case.

Benoit
  • 76,634
  • 23
  • 210
  • 236
3

Switch statements requires that the case values are known at compile time.

The reason why it seems to work when you remove the extern is that you define a constant zero.

Lindydancer
  • 25,428
  • 4
  • 49
  • 68
  • Why it is required to know it at compile time? Why the linker cann't manage this case with switch statement? – Unicorn Apr 07 '11 at 09:50
  • 1
    @Romeno: I suppose that the switch statement purpose is to generate assembly code that does not perform comparisons, but directly jumps to an instruction depending on a value. Therefore assembly code should be fully generated at compile time. See [this link](http://www.eventhelix.com/realtimemantra/basics/CToAssemblyTranslation3.htm) which seems to cover the subject. – Benoit Apr 07 '11 at 09:54
  • 1
    Because compiler has to generate the branch table for switch case using which at the run time it identifies which address it should jump to given a specific value – mukeshkumar Apr 07 '11 at 09:55
  • 3
    The standard requires: "_The expression of each case label shall be an integer constant expression **and no two of the case constant expressions in the same switch statement shall have the same value** after conversion_". If the expressions are not compile-time constants, the compiler has no way of telling whether these constraints are met. One of the compiler's task is to assert that a program is well-formed. The linker doesn't care, it only needs to make a binary out of what the compiler throws at it, and it relies that the compiler won't lie to it. – Damon Apr 07 '11 at 09:55
  • @Damon: Ok... Is there any cases when linker modifies/generates assembly code itself? I still want to understand why linker cant manage this task.. – Unicorn Apr 07 '11 at 10:01
  • Switch statements are designed to allow the compiler to generate effective code *for the constants provided*. The code could be a direct jump (if the case values are dense), a binary search tree, or anything else it can think of. This this nothing that can be deferred to the linker, as a linker typically can't change code that much. The best a compiler/linker could do with unknown values is exactly what you can do with an `if ... else if .. else if` construct. – Lindydancer Apr 07 '11 at 10:14
  • @Damon There's probably a bit of a vicious circle involved. Linkers (and the object file format, which doesn't support the information the linker would need) can't handle this case because no programming language requires it, and programming languages don't require it because the linkers cannot handle it. – James Kanze Apr 07 '11 at 10:16
  • 1
    @Romeno: The reason why the linker cannot manage this task is simply that it is not its business. The programmer has an idea and expresses it in source code via a well-defined language. The front end (of which the compiler is a part) reads the source code, verifies that syntax is valid and hands a (implementation dependent) semantic representation to the backend (to which assembler and linker belong). The backend's only task is to turn this "concept" into a binary form that will run on the target CPU (and optionally strip out redundant things without changing the semantics). – Damon Apr 07 '11 at 10:28
  • 1
    @James Kanze: It makes sense, though. If you look at compilers like gcc which have a dozen or so frontends and a dozen backends (or two dozen?), then it becomes obvious how ingenious it is to not have the linker care about seeing any of the information in the sources. The same linker will generate binaries from C, C++, Go, or Fortran, and it doesn't even know. And the same compiler will parse C++ that will run on i86, MIPS, 68k, and whatnot, and it won't know either. Now recently, with the advent of LTO, I wonder how this still works, but apparently it does... – Damon Apr 07 '11 at 10:32
  • 1
    @Damon The linker will see whatever is in the object files. g++ (like every other compiler) will write the object files to the specified format. If the specified format contains whatever information the linker needs, the linker can do the job. If it doesn't (and none that I know do), the linker can't, and the compiler has to do it. (A lot of modern compilers use their own linker format, more like Java Byte code than traditional object format, to support cross module optimization.) – James Kanze Apr 07 '11 at 11:17