2

Originally this had to be the final paragraph of another question (this one), but I noticed that the topics were not so similar and consequently I thought of posting another one.

What I'm trying to accomplish is to make an automated system to compile some actions in an easy way.

For instance, instead of writing

switch (var)
{
    case SM_1:
        printf("Case 1");
        break;
    case SM_2:
        printf("Case 2");
        break;
    case SM_3:
        printf("Case 3");
        break;
}

it would be easier for me to write something like

#define CASES 
#define CreateCaseX(s,l) CASES += \
    case s: \
        printf(l); \
        break;

CreateCaseX(SM_1, "Case 1")
CreateCaseX(SM_2, "Case 2")
CreateCaseX(SM_3, "Case 3")

...

switch(var)
{
    CASES
}

I don't expect the CASES += statement to actually work, but... Is there some similar construct I can use in the preprocessor to add some actions (function calls, switch cases, ...) to a list and then let it write them all at once?

EDIT: I thought I had well fixed my problem boundaries, but looking at some answers and comments I noticed that this question could be interpreted in different ways. So please forgive me, now I'll try to detail things better.

I'm writing a simple state machine. Whenever I add a state, I have to perform different code modifications, such as

  • add the state to the enumeration
  • add the state name in the switch case construct that returns the name of the current state
  • add the action to be performed when that state is the current one

For instance, I can have these lines in my file:

enum {
    SM_Case_1,
    SM_Case_2,
    SM_Case_3
} currentState;

void printState()
{
    switch (currentState)
    {
    case SM_Case_1:
        printf("State 1");
        break;
    case SM_Case_2:
        printf("State 2");
        break;
    case SM_Case_3:
        printf("State 3");
        break;
    }
}

void executeAction()
{
    switch (currentState)
    {
    case SM_Case_1:
        // Do nothing
        break;
    case SM_Case_2:
        globalVariable += 10;
        break;
    case SM_Case_3:
        printf("Error");
        break;
    }
}

It is much easier (and easier to maintain) in my opinion to have a macro dealing with this, such as:

#define ENUMS
#define NAMES_CASES
#define ACTION_CASES
#define CreateState(s,l,a) \
    ENUMS += s, \
    NAMES_CASES += case s: printf(l); break; \
    ACTION_CASES += case s: a; break;

CreateState(SM_Case_1,"State 1",{})
CreateState(SM_Case_2,"State 2",globalVariable += 10)
CreateState(SM_Case_3,"State 3",printf("Error"))

enum {
    ENUMS
} currentState;

void printState()
{
    switch (currentState)
    {
    NAMES_CASES
    }
}

void executeAction()
{
    switch (currentState)
    {
    ACTION_CASES
    }
}

I noticed that it was tricky to write a feasible syntax, so probably such a technique does not exist, but... If there is, it's useful to know..

frarugi87
  • 2,826
  • 1
  • 20
  • 41

2 Answers2

5

There is no general way to accumulate strings in macros, but you can effectively define lists by building a sequence of macro invocations as a macro. That has to be done in one piece, but it seems like that would work with your example.

Once you've built the list, you can use it more than once for different purposes. This technique is commonly referred to as X macros.

Here's a simple example. (I personally prefer the style where the macro name is passed to the X macro, rather than hard-coding the macro name X as in the Wikipedia article.)

// Make the list (I added semicolons to the actions.)
#define STATES(X)                              \
  X(SM_Case_1,"State 1",{})                    \
  X(SM_Case_2,"State 2",globalVariable += 10;) \
  X(SM_Case_3,"State 3",printf("Error");)

//...

#define ENUM(S, L, A) S,    
enum {
    STATES(ENUM)
} currentState;
#undef ENUM

// ...

#define CASE(S, L, A) case S: printf("%s", L); break;    
void printState() {
    switch (currentState) {
    STATES(CASE);
    }
}
#undef CASE

// ...

#define CASE(S, L, A) case S: { A } break;
void executeAction() {
    switch (currentState) {
    STATES(CASE);
    }
}
#undef CASE

Note that the latter two stanzas are similar enough that they could be encapcsulated in a macro like:

#define SWITCH_FUNC(NAME, CASE) \
  void NAME() { switch (currentState) { STATES(CASE); } }

Also, you could use token concatenation and stringification to simplify the list to two arguments instead of three:

#define STATES(X)                   \
  X(State_1, {})                    \
  X(State_2, globalVariable += 10;) \
  X(State_3, printf("Error");)

#define CONCAT_(A, B)    A##B
#define STRINGIFY_(S)    #S
#define CASENAME(S)      CONCAT_(SM_, S)
#define ENUM(S,A)        CASENAME(S),
#define PRINT_CASE(S,A)  case CASENAME(S): printf("%s", STRINGIFY_(S)); break;
#define ACTION_CASE(S,A) case CASENAME(S): { A } break;

(Live on coliru, although the preprocessor output is a bit hard to read)

Occasionally you will want to use preprocessor conditions to control whether individual items are placed into the list. Refer to the second part of this answer for a nifty trick to accomplish that.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Never heard of X macros, and I think these are exactly what I was looking for... Let me have a look at them and try to implement some variations and I'll give back a feedback. Thank you – frarugi87 Mar 24 '18 at 15:54
  • @frarugi87: Edited my answer on the basis of your edit. – rici Mar 24 '18 at 16:48
2

you cannot but what's the difference with this valid code?

#include <stdio.h>

#define SM_1 1
#define SM_2 2
#define SM_3 3

#define CreateCaseX(s,l) case s: \
        printf(l); \
        break

#define CASES \
CreateCaseX(SM_1, "Case 1"); \
CreateCaseX(SM_2, "Case 2"); \
CreateCaseX(SM_3, "Case 3")

void test(int var)
{

   switch(var)
   {
      CASES;
   }
}

just define CASES with your 3 macros. That's the closest of a list you can get.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • This does solve the question as it was written, but my problem is quite more complex (see edit). In any case, I hadn't thought of "reversing" the macro; this will be very useful in other problems I have. Thank you +1 – frarugi87 Mar 24 '18 at 15:52