-4

following this question Why doesn't gcc allow a const int as a case expression?, basically the same as What promoted types are used for switch-case expression comparison? or Is there any way to use a constant array with constant index as switch case label in C?.

From the first link, I tried to replace :

case FOO:                       // aka 'const int FOO = 10'

with :

case ((int) "toto"[0]):         // can't be anything *but* constant

Which gives :

https://ideone.com/n1bmIb -> https://ideone.com/4aOSXR = works in C++

https://ideone.com/n1bmIb -> https://ideone.com/RrnO2R = fails in C

I don't quite understand since the "toto" string can't be anything but a constant one, it isn't even a variable, it lies in the void of the compiler memory. I'm not even playing with the 'const' fuzzy logic of the C language (that really stands for "read-only, not constant, what did you expect?"), the problem is either "array access" or "pointer referencing" into a constant expression that do not evaluate in C, but do quite well in C++.

I expected to use this "trick" to use a HASH_MACRO(str) to generate unique case labels values from a key identifier, leaving eventually the compiler to raise an error in case of collision because of similar label values found.

OK, ok, I was told these restrictions were made to simplify language tooling (preproc, compiler, linker) and C ain't no LISP, but you can have full featured LISP interpreter/compilers for a fraction of the size of a C equivalent, so that's no excuse.

Question is : is there an "extension" to C11 that just allows this "toto" thingy to work in GCC, CLANG and... MSVC ? I don't want to go the C++ path (typedef's forward declarations don't work anymore) and because embedded stuff (hence the compile-time hash computation for space-time distortion).

Is there an intermediary "C+" language that is more 'permissive' and 'understand' embedded a little better, like -Praise the Lords- "enums as bitfield members", among nice other things we cannot have (because of out-of-reality standards evolving like snails under a desert sun) ?

#provemewrong, #changemymind, #norustplease

Kochise
  • 504
  • 5
  • 10
  • I'm not sure why this got downvoted - especially not without any comments. It's actually an interesting question that requires some thought and knowledge of the C standard in order to answer. Someone who doesn't already know why it doesn't work isn't going to know where to look to get the answer. – Andrew Henle May 24 '18 at 14:04
  • The TL;DR answer would be: because the definition of an integer constant expression in C is flawed. In C++ they fixed it. – Lundin May 24 '18 at 14:08
  • @Lundin : they could have "fixed" the C since the standard evolved, some compilers adding "extensions". I mean, this is so "basic" I cannot understand they refused this into C. This is no rocket science and we then have to dig a treasure trove of preprocessor meta programming to simulate the desired effect. Or not. Why something like M4, or even a lighter macro evaluation like a LISP derivative, not included into the preprocessing or even the compiler ? I mean, how many times I had to rely on a pre processing step to sort arrays, while a constexp qsort(myshit) should have done the trick. – Kochise May 24 '18 at 14:28
  • 1
    @AndrewHenle I notice that a user having almost no reputation gets very fast down-voted while users with higher reputation [~>2k] - _even for question where I ask myself_ - get quickly up-voted. I up-voted the question because I think it is good and something else in comparison to some _beginner_ questions. – Peter VARGA May 24 '18 at 14:36
  • 1
    @Al Bundy : thanks, I don't use stackoverflow often, as I find it... *overflowed* with basic questions. I have a pretty good track record on Codeproject on the other hand, so I often find my luck there. But as they are more C++ or C# experts, and my question specifically targets C, I came here. And I don't care about ragedownvotes, that doesn't affect my life, provided good guys like you are more elaborates and constructives. – Kochise May 24 '18 at 14:42
  • @AndrewHenle I didn't downvote it but if I did, I wouldn't downvote it due to the low reputation but rather because of all the personal ranting in the latter half of it and for it lacking a proper justification for why it needs to be exactly this way. – Antti Haapala -- Слава Україні May 24 '18 at 15:56
  • @Antti Haapala Because a constant string literal should be treated as is, a constant, as strings and array accesses being already part of the C standard, it's baffling those aren't accessible from everywhere within the language. The lack of orthogonality render things rather hard to deal with. I mean, I just wanted to port a C++ macro trick into C, and this *simple* limitation prevent me from. Some things should have evolved evenly between C and C++ to keep some consistency between those two, but they seems to diverge more and more into two distinctly separate same looking incarnations. Sigh. – Kochise May 25 '18 at 07:05
  • 2
    All in all, Stack Overflow questions and comments are not a proper place for a manifesto. I suggest that you write an article about this elsewhere. – Antti Haapala -- Слава Україні May 25 '18 at 07:35
  • Adding a link to a possible solution, but not : https://stackoverflow.com/questions/6190963/c-macro-to-convert-a-string-to-list-of-characters – Kochise May 25 '18 at 08:42
  • @Antti Haapala Not a manifesto per se, but worth a read http://blog.robertelder.org/7-weird-old-things-about-the-c-preprocessor/ Btw thanks for the help and the "solution" you provided even though it doesn't change a thing to my problem – Kochise May 25 '18 at 08:57
  • Problems close to mine, for reference : https://stackoverflow.com/questions/20121278/c-preprocessor-to-split-int-x-into-int-x and https://stackoverflow.com/questions/45848866/pre-processor-macro-to-convert-an-hex-string-to-a-byte-array – Kochise May 25 '18 at 11:40

3 Answers3

6

It doesn't matter whether or not it could be known to the compiler at compile time. The case label needs to have a value that is an integer constant expression (C11 6.8.4.2p3).

  1. 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. There may be at most one default label in a switch statement. (Any enclosed switch statement may have a default label or case constant expressions with values that duplicate case constant expressions in the enclosing switch statement.)

And the definition of an integer constant expression is in C11 6.6p6:

  1. 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.

Since "toto" is none of integer constants, enumeration constants, character constants, constant sizeof, _Alignof expressions or floating point constant cast to an integer; and that list was specified in the constraints section of the standard, the compiler must not pass this silently. (Even a conforming compiler may still successfully compile the program, but it must diagnose this as a constraint violation.)


What you can use is chained ? : to resolve the index to a character constant, i.e.

  x == 0 ? 't' 
: x == 1 ? 'o'
: x == 2 ? 't'
: x == 3 ? 'o'

This can be written into a macro.

Ilja Everilä
  • 50,538
  • 7
  • 126
  • 127
  • this cannot be declared into a macro, since the HASH_MACRO(str) generates something like this : case ( ( ( (('\0' == "toto"[0]) ? ((cnU32) 0) : "toto"[0] + ((('\0' == "toto"[0 +1]) ? (65599ULL) : "toto"[0 +1] + ((('\0' == "toto"[0 +1 +1]) ? (65599ULL) : "toto"[0 +1 +1] + ((('\0' == "toto"[0 +1 +1 +1]) ? (65599ULL) : "toto"[0 +1 +1 +1] + ((('\0' == "toto"[0 +1 +1 +1 +1]) ? (65599ULL) : "toto"[0 +1 +1 +1 +1] + ((('\0' == "toto"[0 +1 +1 +1 +1 +1]) ? (65599ULL) : "toto"[0 +1 +1 +1 +1 +1] + ((('\0' == "toto"[0 +1 +1 +1 +1 +1 +1]) ? (65599ULL) : "toto"[0 +1 +1 +1 +1 +1 +1] + ... – Kochise May 24 '18 at 14:16
  • You need to give the individual characters as arguments. – Antti Haapala -- Слава Україні May 24 '18 at 14:19
  • "You need to give the individual characters as arguments." -> No, just... no. We're in 2018, some things should have improved over the last 40 years, and string handling into preprocessor and compiler should be one. Even Python3 improved in that regard. – Kochise May 24 '18 at 14:23
  • 4
    You can take that up with the C standards committee. – Christian Gibbons May 24 '18 at 14:41
  • @Christian Gibbons : only if there's a Nobel prize to win. No, seriously, I mean there's no luck this C++ feature to be back ported into C. That would be too obvious. And if ever I tried, that wouldn't be before C39. – Kochise May 24 '18 at 14:45
  • @Kochise Changing this changes the fundamental way some things are compiled. It's not a matter of features being ported to C, it's that C has a specific behaviour. – Thomas Jager May 24 '18 at 17:29
  • @Thomas Jager I don't want the legacy behaviors of C being changed inside out, I just need the C preprocessor and compiler to use "string" and array[x] in constant expressions evaluation, not just integer constants. It's adding a feature, not changing what's already working into something else. Nothing really fancy. – Kochise May 25 '18 at 07:08
  • In a C compiled binary the exact same string literal `const char *` won't even always be the same memory address, *usually* it will be... but you can set unusual compiler flags or link separate object files and wind up with different literal addresses for what looks like the same string. I am guessing that this may be one justification that the standard doesn't allow you to use literal strings in `case` statements; to protect the compiler from the linker and vice versa, and unfortunately the designers of C did not forsee the implications for "meta programming". – szmoore May 11 '21 at 10:30
  • @szmoore : Indeed I guess. I moved on since. The solution is using the C++ compiler as a C compiler since they added designated initializers as well (with more limitations though, like initialization order). – Kochise Jul 19 '21 at 08:32
2

"toto[0]" is not an integer constant expression as C defines the term:

6.6 Constant expressions
...
6    An integer constant expression117) 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.
117) An integer constant expression is required in a number of contexts such as the size of a bit-field member of a structure, the value of an enumeration constant, and the size of a non-variable length array. Further constraints that apply to the integer constant expressions used in conditional-inclusion preprocessing directives are discussed in 6.10.1.

C 2011 online draft

Community
  • 1
  • 1
John Bode
  • 119,563
  • 19
  • 122
  • 198
0

The issue that you're running into is that, in C, "toto" is an array of chars. Sure, it's constant in memory, but it's still just an array. The [] operator indexes in an array (from a pointer). If you wanted, you would be able to edit a compiled binary and change the string "toto" to something else. In a sense, it is not compile-time known. It's equivalent to doing:

char * const ___string1 = "toto";
...
case ((int) ___string1[0]):

(This is a little forced and redundant, but it's just for demonstration)

Note that the type of the elements of a string literal is char, not const char.

The case, must be a constant however, as it is built into the compiled program control flow.

Thomas Jager
  • 4,836
  • 2
  • 16
  • 30
  • The problem is that I *specifically* don't want the "toto" string to be anywhere into my binary file. I *just* don't need it, I *just* need an unique identifier (here a case label) without having to maintain a large enumeration, which on the other hand *is* C compatible as considered an integer constant. – Kochise May 24 '18 at 14:08
  • @Kochise That's just not the way C works. You're telling it that you want that char array to exist. Any time you use a string literal, you're really using a pointer. Enums and their named values are respectively essentially typedefs of integer types and #defines of integer constants. They aren't a special at all. – Thomas Jager May 24 '18 at 17:34
  • Then tell me if I'm wrong, but string literals are part of the C standard, the preprocessor can even collate them ("Hello " "World!") so it *is* possible to scan through those. Again, array access is also part of the language, I don't understand why isn't not available from the preprocessor or even at compile time to process constant expressions. I mean, it's not like the purposed functions aren't there in the first place. And that C *was* made that way shouldn't prevent it from evolving and easing the development process instead to remains at the dark ages of computer languages. – Kochise May 25 '18 at 07:00
  • The collation of strings is a preprocessor function. It is fine because, since the strings are pointers, putting two strings adjacent is not something you can compile. The preprocessor doesn't care that you're in a switch-case, that's not it's job. – Thomas Jager May 25 '18 at 12:09
  • Yeah, fine, then tell me why you can't explicitly do pointer things into the preprocessor (ie. '*((char*) "toto" + 2)' or '"toto"[2]') but the preprocessor can do it implicitly like in to collation you mentioned ? Things like that are available in C++11, but not into C11, while not targeting C++ specific stuff (classes, templates, whatever) and should have been back ported into C11 to at least have an illusion of consistency between the two languages. I know, I'm complaining, but that such normalization committees meet at regular point in time and are unable to address real life problems is... – Kochise May 25 '18 at 13:31