ashishsony
ashishsony

Reputation: 2587

What is the need of defining an Enum/Struct by way of macros?

I am referring to a code sample from the opensource project tig. Which is a great tool!

file:tig.c

I am struggling to find a reason for defining the request enumeration as follows:

enum request {
#define REQ_GROUP(help)
#define REQ_(req, help) REQ_##req

        /* Offset all requests to avoid conflicts with ncurses getch values. */
        REQ_UNKNOWN = KEY_MAX + 1,
        REQ_OFFSET,
        REQ_INFO,

        /* Internal requests. */
        REQ_JUMP_COMMIT,

#undef  REQ_GROUP
#undef  REQ_
};

even structures as well..

static const struct request_info req_info[] = {
#define REQ_GROUP(help) { 0, NULL, 0, (help) },
#define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) }
        REQ_INFO
#undef  REQ_GROUP
#undef  REQ_
};

as can be seen REQ_GROUP has been #defined multiple times creating confusion.. atleast to me. Well i know there might be a good reason to do as such.. what is the actual reason to hide the enum/struct definition in the code using macros?

Upvotes: 7

Views: 895

Answers (3)

KBart
KBart

Reputation: 1608

You have missed important defines in the same file:

#define REQ_INFO \
REQ_GROUP("View switching") \
VIEW_INFO(VIEW_REQ), \
\
REQ_GROUP("View manipulation") \
REQ_(ENTER, "Enter current line and scroll"), \
REQ_(NEXT, "Move to next"), \
REQ_(PREVIOUS, "Move to previous"), \
< output omitted as it is too long >

So, for example, the structure you have shown expands into:

static const struct request_info req_info[] = {
   { 0, NULL, 0, "View switching" },
   < VIEW_INFO also expands to some long structure that was ommited here >
   { 0, NULL, 0, "View manipulation" },
   { REQ_ENTER, ENTER, STRING_SIZE("ENTER"), "Enter current line and scroll"},
   { REQ_NEXT, NEXT, STRING_SIZE("NEXT"), "Move to next"}
   < and so on >
};

As other answers mentioned, it is done mostly to keep multiple structures/enumerators synchronized.

Upvotes: 1

Mike Seymour
Mike Seymour

Reputation: 254751

It's to avoid duplication of the list of requests. That list only needs maintaining in one place, the definition of REQ_INFO, and the enumeration and data structures are automatically generated from that list by suitable definitions of REQ_GROUP and REQ_.

Without those macros, the enumeration and the data structure would have to be maintained separately, taking care to keep them consistent with each other, involving more work and more scope for error.

Upvotes: 1

Matthieu M.
Matthieu M.

Reputation: 300349

This is usually down when different treatment should be used on the same data source.

For example, you might do:

#define MY_LIST X(Elem1) X(Elem2) X(Elem3)

And then:

enum MyEnum {
# define X(e) e,

  MY_LIST

  Last

# undef X
};

In which case, MY_LIST is expanded using the current definition of X.

Now, in the same file, I can also use MY_LIST to create a to_string method

char const* to_string(MyEnum e) {
    switch(e) {
#       define X(e) case e: return #e;

        MY_LIST

        case Last: return "Last";

#       undef X
    }
    return 0;
} // to_string

This way, the set of values of the enum is only ever written once, and automatically both the enum and a number of methods dealing with it are kept in sync with this set.

Upvotes: 7

Related Questions