defining same elements in X macro C

119 Views Asked by At

I have been exploring X macro and I created this table

#define FAULT_TABLE(FAULT) \
FAULT(INVALID,                            FAULT_CATEGORY_NONE, action_none) \
FAULT(COMMS_FAILURE,                       FAULT_CATEGORY_1, action_1) \
FAULT(QUEUE_FAILURE,                        FAULT_CATEGORY_1, action_1) \
FAULT(SENSOR_FAILURE,                FAULT_CATEGORY_1, action_1) \
FAULT(FAILED_OP                     FAULT_CATEGORY_1, action_1) 

I just wanted to know if I can have same values and generate an enum out of it . I meant the enum out of 2nd column in the table .

And how do I assign the same function call to the fault codes above

Update Just to add more clarity and context: When any of these fault is reported , my fault handler would be looking at the category and function to execute . Some faults have the same action and belong to same category . So I am trying to figure if this can be done through X macro .

So I just want enum to expanded as

typedef enum {
FAULT_CATEGORY_NONE,
FAULT_CATEGORY_1,
FAULT_CATEGORY_N,
}

Also I would the functions defined in table to expanded as Jump table.

1

There are 1 best solutions below

8
John Bollinger On

I just wanted to know if I can have same values and generate an enum out of it . I meant the enum out of 2nd column in the table .

You cannot duplicate enum constants, but you can give different enum constants the same value. So, for example, you could do this:

#define FAULT_CATEGORY_NONE 0
#define FAULT_CATEGORY_1 1

#define FAULT_TABLE(FAULT) \
FAULT(INVALID,                            FAULT_CATEGORY_NONE) \
FAULT(COMMS_FAILURE,                       FAULT_CATEGORY_1) \
FAULT(QUEUE_FAILURE,                        FAULT_CATEGORY_1) \
FAULT(SENSOR_FAILURE,                FAULT_CATEGORY_1) \
FAULT(FAILED_OP,                     FAULT_CATEGORY_1)

#define FAULT(x, y) x ## _CAT = (y),

enum category {
FAULT_TABLE(FAULT)
DUMMY_CAT
};

That expands to

enum category {
    INVALID_CAT = (0),        COMMS_FAILURE_CAT = (1), QUEUE_FAILURE_CAT = (1),
    SENSOR_FAILURE_CAT = (1), FAILED_OP_CAT = (1),     DUMMY_CAT
};

(extra whitespace added for clarity).

But I don't see how X macros are actually helping you there. In particular, it's suspicious that you present a different definition of FAULT_TABLE for the sub-question about assigning functions.

The X macro approach provides some traction when you can re-use the same list (FAULT_TABLE, here) for multiple definitions of X (FAULT). Your example does not appear to be well suited to that, however, as each use appears to require the list to provide different data. I guess you could cram it all into the same list by relying on FAULT to accept an additional parameter, but you would probably be better off just creating regular tables. For example:

enum fault {
    INVALID, COMMS_FAILURE, QUEUE_FAILURE, SENSOR_FAILURE, FAILED_OP 
};

const int fault_category[] = {
    [INVALID] = FAULT_CATEGORY_NONE,
    [COMMS_FAILURE] = FAULT_CATEGORY_1,
    [QUEUE_FAILURE] = FAULT_CATEGORY_1,
    [SENSOR_FAILURE] = FAULT_CATEGORY_1,
    [FAILED_OP] = FAULT_CATEGORY_1
};

const void (*fault_action[])(void) = {
    [INVALID] = action_none,
    [COMMS_FAILURE] = action_1,
    [QUEUE_FAILURE] = action_1,
    [SENSOR_FAILURE] = action_1,
    [FAILED_OP] = action_2
};

You could certainly employ X macros to generate such code, but I don't think you would save much space, and I think the result would be considerably less clear.


With respect to the question update

You seem to be looking to use the X macro approach to select and generate output only for each distinct value of some argument to the X macro on some given pass through the list. I hesitate to say it's impossible, because clever people have squeezed some fairly surprising behaviors out of the preprocessor, but I am confident in saying that there is no straightforward way to make the preprocessor do it.

The jump table is easier, because you want analogous output for each invocation of FAULT. Thus, you might do this ...

#define FAULT_TABLE(FAULT) \
FAULT(INVALID,                            FAULT_CATEGORY_NONE, action_none) \
FAULT(COMMS_FAILURE,                       FAULT_CATEGORY_1, action_1) \
FAULT(QUEUE_FAILURE,                        FAULT_CATEGORY_1, action_1) \
FAULT(SENSOR_FAILURE,                FAULT_CATEGORY_1, action_1) \
FAULT(FAILED_OP,                     FAULT_CATEGORY_1, action_1) 

#define ENUM_CONST(fault, cat, action)   fault,
#define ACTION_ENTRY(fault, cat, action) [fault] = action,

enum fault {
    FAULT_TABLE(ENUM_CONST)
    DUMMY_FAULT
};

const void (*fault_action[])(void) = {
    FAULT_TABLE(ACTION_ENTRY)
    0
};

But again, I don't see much advantage there over just writing the enum and jump table directly.