简体   繁体   中英

Let a macro count its invocations

I've a huge C project with a module reading and managing configuration data. If I have to add a new configuration parameter, I'll have to edit several functions, eg as pseudo-code:

void read_configuration(config *c) {
    read_param("p1", c->p1);
    read_param("p2", c->p2);
    read_param("p3", c->p3);
    /* ... */
}

void dump_configuration(config *c) {
    dump_param("p1", c->p1);
    dump_param("p2", c->p2);
    dump_param("p3", c->p3);
    /* ... */
}

Is there a way to ensure by macro at compile time , that each location has at least the same count of parameters? I thought of making dump_param some kind of macro counting the invocations and then add something like

#if nr_read != nr_dump
    #error "You forgot something, idiot!"
#endif

at the end of the module. I can't find a method to make the macro count its invocations, though...

Since the list of parameters is the same in both functions, how about factoring that out and avoid any possible mismatch ?

Using X-macros

#define X_CONFIG_PARAMS(config) \
    X("p1", (config).p1)        \
    X("p2", (config).p2)        \
    X("p3", (config).p3)

void read_configuration(config *c) {
#define X(name, param) read_param(name, &param);
    X_CONFIG_PARAMS(*c)
#undef X
}

void dump_configuration(config *c) {
#define X(name, param) dump_param(name, &param);
    X_CONFIG_PARAMS(*c)
#undef X
}

Using function pointers

void alter_config(config *c, void(*func)(char const *name, Param *param)) {
    func("p1", &c->p1);
    func("p2", &c->p2);
    func("p3", &c->p3);
}

void read_configuration(config *c) {
    alter_config(c, read_param);
}

void dump_configuration(config *c) {
    alter_config(c, dump_param);
}

Using an array and offsetof

struct param_info {
    char const *name;
    size_t config_offs;
};

param_info allParams[] = {
    {"p1", offsetof(config, p1)},
    {"p2", offsetof(config, p2)},
    {"p3", offsetof(config, p3)}
};

void read_configuration(config *c) {
    size_t paramCount = sizeof allParams / sizeof *allParams;
    for(size_t i = 0; i < paramCount; ++i) {
        Param *p = (Param*)((char*)config + allParams[i].config_offs);
        read_param(allParams[i].name, p);
    }
}

void dump_configuration(config *c) {
    size_t paramCount = sizeof allParams / sizeof *allParams;
    for(size_t i = 0; i < paramCount; ++i) {
        Param *p = (Param*)((char*)config + allParams[i].config_offs);
        dump_param(allParams[i].name, p);
    }
}

I would rather let the preprocessor write the code in the first place.

It could look something like this:

Define the list of parameters in a separate file, say parameters.inc :

PARAM (p1)
PARAM (p2)
...

Then in the source code locally define the macro PARAM as required and let the preprocessor include and expand the contents of parameters.inc :

void read_configuration(config *c) {
#define PARAM(NAME) read_param(#NAME, c->NAME);
#include "parameters.inc"
#undef PARAM
}

void dump_configuration(config *c) {
#define PARAM(NAME) dump_param(#NAME, c->NAME);
#include "parameters.inc"
#undef PARAM
}

I don't think you can do this at compile time without ugly hacks.

What you could do: add a test to your test suite which replaces the header that contains the read_param() and dump_param() macros so they generate code which only updates a counter. Then, in the main() function of that test, place an assertion that compares both counters and fails if they're not equal.

You do have a test suite and run it at compile time, right? ;-)

However, I do agree with the comment that it's probably better to do this differently. In an approach called "table-driven programming", you turn the macro definition and data definition on their head (that is, you have the #define in your .c file and the use of the macro in the header rather than the other way around), you don't have this problem. Poul-Henning Kamp, of FreeBSD fame, explains very well how to that here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM