Reputation: 2587
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
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
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
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