4

I have a question for a quick workaround to enjoy the benefits of the non-standard gnu case ranges. For example, the non-standard:

case 1 ... 5:

Could be replaced by:

case 1:
case 2:
case 3:
case 4:
case 5:

Probably some macro solution might be in order. From my memory macro loops cannot loop for large numbers of iterations. For this reason, what if the range is "large", say in the thousands?

user1095108
  • 14,119
  • 9
  • 58
  • 116
  • 5
    use `if..else` or other such constructs – M.M Nov 26 '15 at 08:00
  • Why? I could very well obtain an equivalent by supplying all case labels in the range. – user1095108 Nov 26 '15 at 08:02
  • Because it's much simpler . Imagine working on a source file with thousands of lines of `case` ... – M.M Nov 26 '15 at 08:03
  • @M.M That's why I'm asking for a (probably) macro-based alternative. – user1095108 Nov 26 '15 at 08:04
  • Why even bother when you can just solve the problem trivially with an if..else ? – M.M Nov 26 '15 at 08:04
  • @M.M For example, when porting a program that contains this non-standard construct. – user1095108 Nov 26 '15 at 08:05
  • If ranges are wanted, then one could probably mash something up using classes with chained functions taking a value or a range as first argument, and the function to call (which could of course be a lambda) as second argument. It's not the same as a `switch` statement, and the compiler might not optimize it as well (not possible to use jump tables etc.), but at least it would be pure C++ and standards compliant as well. – Some programmer dude Nov 26 '15 at 08:05
  • 4
    Replacing it with a standard construct will mean you never run into the problem again when porting the code to another platform – M.M Nov 26 '15 at 08:05

2 Answers2

2

If you're talking preprocessor loops I guess that you're thinking of the preprocessor meta programming from boost. While it's probably quite portable, the loops seems to be limited to 255 "iterations". In fact the implementation is not a real loop, it's more like a hard coded loop-unroll (thereby the limitation). You could of course expand this to more iterations.

While the preprocessor trick could be tempting, I think you should consider using if-else if-else construct. What's actually (often) happens in a modern compiler regarding conditionals is that it boils down to the same construct that should generate the same code (unless you trick the compiler into evaluating the variable multiple times).

You could even combine the constructs, using a switch-case construct for all singular alternatives and then after the default label add an if-else if-else to handle all ranges.

A third solution would be to write a script that finds the case-ranges and replace them by a standard construct, this should be fairly straight forward in most cases as case can't appear in many places without being a keyword and then it should be followed by an expression which can't contain ... in that way. The only problematic situation (that I can think of) would be when the case-range is a result of preprocessor expansion.

skyking
  • 13,817
  • 1
  • 35
  • 57
2

The best alternative would be to re-factor the code to use if/else. If there truly are thousands of cases it may or may not be very efficient to have a giant case statement in the first place.

However, it because cases could "fall-through" or other odd flow control like Duff's device (I hope not), it may not be completely a straightforward conversion.

It is not likely to be a very good implementation to abuse the preprocessor to "loop". See Writing a while loop in the C preprocessor for sample of what this might look like.

It may be best to write a simple python or awk script. However this approach may also be flawed if the keyword case appears somewhere like a string or if labels the preprocessor changes anything. This may work very well for a narrow one-off conversion though, but without seeing the code in question it is hard to say.

There is a serious problem with either preprocessing approach if the case labels use enumerations. Since enums are still just text strings at the time the preprocessor runs (or an external script), how can it iterate from STATE_10 to STATE_20 without knowing what integers they represent? It can't - the GNU extension really requires compiler support.

If a one-time wholesale replacement of the case statement is too invasive or irregular to manage, you could probably utilize a hybrid approach:

Assuming you have a (notional) example like:

switch(state)
{
case STATE_1:
    xxx; break;
case STATE_2 ... STATE_10:
    yyy; break;
}

Allocate a previously unused range of indexes. Add one new special index for each existing range label. Use if/else logic to detect the ranges first and then replace the range case with a new standard one. This allows the control flow structure to remain essentially unmodified:

#if !defined(__GNUC__)

#define STATE_RANGE_2_10 101

if(state >= 2 && state <= 10)
    state2 = STATE_RANGE_2_10
else if(...)
    state2 = STATE_RANGE_x_y
else
    state2 = state;

#else /* GNU */

#define STATE_RANGE_2_10 STATE_2 ... STATE_10
state2 = state;

#endif

switch(state2)
{
case STATE_1:
    xxx; break;
case STATE_RANGE_2_10:
    yyy; break;
}

With some suitable macros this could even be made portable between GNUC and real C if you really wanted GNUC to still use the extension for some reason. Note: I introduced the state2 variable in case it is stored or used outside the local scope. If not, you can skip that.

Community
  • 1
  • 1
Anders
  • 2,270
  • 11
  • 19