0

I have the following code

#define SWITCH(S) char *_S = S; if (0)
#define CASE(S) } else if (strcmp(_S, S) == 0) {switch(1) { case 1
#define BREAK }
#define DEFAULT } else {switch(1) { case 1

int main()
{
    char buf[256];

    printf("\nString - Enter your string: ");
    scanf ("%s", buf);

    SWITCH (buf) {
        CASE ("abcdef"):
            printf ("B1!\n");
            BREAK;
        CASE ("ghijkl"):
            printf ("C1!\n");
            BREAK;
        DEFAULT:
            printf ("D1!\n");
            BREAK;
    }
}

If I generate the pre-processor code with gcc -E, I will get the following code

int main()
{
    char buf[256];

    printf("\nString - Enter your string: ");
    scanf ("%s", buf);

    char *_S = buf;
    if (0) {
    } else if (strcmp(_S, "abcdef") == 0) {switch(1) { case 1:
        printf ("B1!\n");
        };
    } else if (strcmp(_S, "ghijkl") == 0) {switch(1) { case 1:
        printf ("C1!\n");
        };
    } else {switch(1) { case 1:
        printf ("D1!\n");
        };
    }
}

But for some gcc defining char *_S = buf; in the middle of the code is not wolcome and could provide compilation error

How to fix that in my macro?

Please do not suggest to define char *_S as global (out of the main)

MOHAMED
  • 41,599
  • 58
  • 163
  • 268
  • What exactly do you want your macro to generate? why will `char *_S = buf;` be an error? Do you mean in case `_S` is already defined? As suggested by unwind the best way is to get rid macros. – another.anon.coward Jan 08 '13 at 16:17
  • @another.anon.coward for some gcc defining variable should be at the top of the function otherwise they generate error – MOHAMED Jan 08 '13 at 16:18
  • 1
    It has to be at beginning for a block, not necessarily a function. As pointed out in unwind's answer add `{` to `SWITCH` & another `}` by introducing `SWITCH_END` – another.anon.coward Jan 08 '13 at 16:21
  • I think "for some gcc" means using switches like `-std=c89 -pedantic` or similar. – aschepler Jan 08 '13 at 16:23
  • 1
    Those macros could come back to bite you during a debugging session at some point. It's the reason I avoid macros as much as possible. The code you see isn't the same code the debugger sees. – dutt Jan 08 '13 at 16:39
  • 2
    Given the funny restrictions your SWITCH control structure currently has (can only be used once in each block; must have a BREAK on every case so no fall-through and in particular therefore no consecutive cases sharing code; uses reserved names), I don't think one more funny restriction will make it any harder to use. Put an extra block around the whole of each use of it, like `{ SWITCH(S) { ... } }`. – Steve Jessop Jan 08 '13 at 16:44
  • 4
    oh god, this is so horrible :( – Andreas Grapentin Jan 08 '13 at 16:55

3 Answers3

8

Remove the macros altogether, and write it in the "expanded" way, moving the declaration to the top. These macros are horrific.

Failing that, tweak SWITCH to introduce a new scope (a second {). This will of course force you to have to close two scopes, so perhaps add a SWITCH_END abomination to use at the end, to encapsulate that. Whatever.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 3
    I feel that you didn't stress the "horrific" strongly enough, but I went ahead and up-voted your answer anyway. – phonetagger Jan 08 '13 at 17:49
8

Oh no!!!

I wrote this as a joke in this post

Don't use it is very very horrific, if you want to avoid if-else you can do it without torturing the compiler, consider using pair of strings:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char buf[256];
    const char *ap[] = {
        "abcdef", "B1!\n",
        "ghijkl", "C1!\n",
        NULL    , "D1!\n",
    }, **p = ap;

    printf("\nString - Enter your string: ");
    scanf ("%s", buf);
    while (*p) {
        if (strcmp(buf, *p) == 0) break;
        p += 2;
    }
    printf("%s", *(++p));
    return 0;
}
Community
  • 1
  • 1
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
1

Make sure the code is being compiled as C99 or later; otherwise, you will need to use a different control structure.

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L

  SWITCH(buf)
  {
    ...
  }

#else

  if (strcmp(buf, "abcdef") == 0)
  {
    ...
  }
  else if (strcmp (buf, "ghijkl") == 0)
  {
    ...
  }
  else
  {
    ...
  }

#endif

It's generally not a good idea to use the preprocessor to "alter" or extend C syntax (I have the scar tissue to prove it); switch isn't defined on string expressions for a reason.

If you really want to use a switch in this situation, then it may be better to code up a hash function that returns a key for each string, and switch on the result:

#define ABCDEF ... // hash key generated for "abcdef"
#define GHIJKL ... // hash key generated for "ghijkl"
...
switch(hash(buf))
{
  case ABCDEF :
     ...
     break;

  case GHIJKL :
     ...
     break;

  default:
     ...
     break;
}
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • +1 for the hashing! although it may be worth mentioning that you'd have to check for collisions. – Andreas Grapentin Jan 08 '13 at 16:52
  • for the hash : there is a collision risk. – MOHAMED Jan 08 '13 at 17:20
  • 1
    Good freaking grief. I'd vote this down because of the hashing but I don't want to waste the points to do so. NO... WAIT... I HAVE THE SOLUTION... Farm the decision out to a remote decision server. MAKE THE DECISIONS IN THE CLOUD! The cloud is the way to go! – phonetagger Jan 08 '13 at 17:35
  • @phonetagger: Jeez, don't let concern over points stop you. You think it's a bad answer, vote it down. Personally, I think the OP should abandon that abomination of a macro and write it up as a simple if-else chain (or build a table lookup, or something). But if they *really* want to use a `switch`, then they should *use a switch*, not a macro that's pretending to be a switch. Hashing the strings is one way to accomplish that. Never claimed it was the *best* way to do so. – John Bode Jan 08 '13 at 17:50
  • "Never claimed it was the *best* way to do so." Ok, I'll give you that. Yes, table lookup would be good, if there are more than a few string comparisons to be made. There's some threshold, different for everyone of course, where such a lookup table would become easier to grok than the corresponding if-else chain. – phonetagger Jan 08 '13 at 17:53
  • @phonetagger hash key macro's like these are sometimes used in game engines when the product is finished and ready to ship. Relatively speaking, comparing strings is expensive. However I'd do this as a last optimization resort :) – rwols Jan 09 '13 at 00:10
  • @rwols - Hmm. OK, thanks. But.... can you point me to a web page or something that describes how hash key macros can be used in game engines for string comparison purposes? I'm having trouble imagining a use case for them that couldn't be satisfied more simply & efficiently by using enums. – phonetagger Jan 09 '13 at 16:50
  • @phonetagger In a game, there are lots of objects that require some kind of unique id in the game world (NPCs, items, vehicles, other stuff). One way of addressing them is assigning a unique unsigned integer to them. However that's annoyingly hard to remember for humans. So we can also assign a unique string value to every object. We can then use a scripting language like python or perl to preprocess every unique string into a precalculated hash and put all the results into a header file, and include that header file in every cpp file that needs to reference objects by their id. – rwols Jan 10 '13 at 22:38
  • @phonetagger I know that Naughty Dog Studios did it with a CRC32 hash at one point. http://stackoverflow.com/questions/8730941/what-are-some-good-methods-to-replace-string-names-with-integer-hashes – rwols Jan 10 '13 at 22:51