I'm studying the code of the netstat tool (Linux), which AFAIK mostly reads a /proc/net/tcp
file and dowa pretty-printing out of it. (My focus is on the -t mode right now.)
I'm a bit puzzled by the coding style the authors have chosen:
static int tcp_info(void)
{
INFO_GUTS6(_PATH_PROCNET_TCP, _PATH_PROCNET_TCP6, "AF INET (tcp)", tcp_do_one);
}
where
#define INFO_GUTS6(file,file6,name,proc) \
char buffer[8192]; \
int rc = 0; \
int lnr = 0; \
if (!flag_arg || flag_inet) { \
INFO_GUTS1(file,name,proc) \
} \
if (!flag_arg || flag_inet6) { \
INFO_GUTS2(file6,proc) \
} \
INFO_GUTS3
where
#define INFO_GUTS3 \
return rc;
and
#if HAVE_AFINET6
#define INFO_GUTS2(file,proc) \
lnr = 0; \
procinfo = fopen((file), "r"); \
if (procinfo != NULL) { \
do { \
if (fgets(buffer, sizeof(buffer), procinfo)) \
(proc)(lnr++, buffer); \
} while (!feof(procinfo)); \
fclose(procinfo); \
}
#else
#define INFO_GUTS2(file,proc)
#endif
etc.
Clearly, my coding sense is tilting and says "those should be functions". I don't see any benefit those macros bring here. It kills readability, etc.
Is anybody around familiar with this code, can shed some light on what "INFO_GUTS" is about here and whether there could have been (or still has) a reason for such an odd coding style?
In case you're curious about their use, the full dependency graph goes like this:
# /---> INFO_GUTS1 <---\
# INFO_GUTS --* INFO_GUTS2 <----*---- INFO_GUTS6
# î \---> INFO_GUTS3 <---/ î
# | |
# unix_info() igmp_info(), tcp_info(), udp_info(), raw_info()
Function-like macros can take arguments, just like true functions. To define a macro that uses arguments, you insert parameters between the pair of parentheses in the macro definition that make the macro function-like. The parameters must be valid C identifiers, separated by commas and optionally whitespace.
The assert macro shall be implemented as a macro, not as an actual function. If the macro definition is suppressed in order to access an actual function, the behavior is undefined.
Macros are generally used to define constant values that are being used repeatedly in program. Macros can even accept arguments and such macros are known as function-like macros. It can be useful if tokens are concatenated into code to simplify some complex declarations.
Your sense that "those macros should be functions" seems correct to me; I'd prefer to see them as functions.
It would be interesting to know how often the macros are used. However, the more they're used, the more there should be a space saving if they're a real function instead of a macro. The macros are quite big and use (inherently slow) I/O functions themselves, so there isn't going to be a speed-up from using the macro.
And these days, if you want inline substitution of functions, you can use inline
functions in C (as well as in C++).
You can also argue that INFO_GUTS2 should be using a straight-forward while
loop instead of the do ... while
loop; it would only need to check for EOF once if it was:
while (fgets(buffer, sizeof(buffer), procinfo))
(*proc)(lnr++, buffer);
As it is, if there is an error (as opposed to EOF) on the channel, the code would probably go into an infinite loop; the fgets()
would fail, but the feof()
would return false (because it hasn't reached EOF; it has encountered an error - see ferror()
), and so the loop would continue. Not a particularly plausible problem; if the file opens, you will seldom get an error. But a possible problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With