Let the macro count its calls

I have a huge C project with a configuration data reader and management module. If I need to add a new config parameter, I have to edit several functions, eg. as pseudocode:

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 that the macro at compile time ensures that each location has at least the same number of parameters? I was thinking about doing dump_param

some kind of macro that counts the calls and then adding something like

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

      

at the end of the module. I can't find a way to get the macro to count its calls, though ...

+3


source to share


3 answers


Since the parameter list is the same in both functions, how about factoring and avoiding 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);
    }
}

      

+6


source


I would prefer that the preprocessor writes the code first.

It might look something like this:

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

:



PARAM (p1)
PARAM (p2)
...

      

Then, in your source code, set the macro locally PARAM

as needed and let the preprocessor include and expand the content 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
}

      

+2


source


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

What you can do: Add a test to your test suite that replaces the header containing macros read_param()

and dump_param()

so that they generate code that only updates the counter. Then, in the function of main()

that test, put a statement that compares both counters and fails if they are not equal.

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

However, I agree with the comment that it's probably better to do it differently. In an approach called "spreadsheet programming" you wrap the macro definition and data definition on your head (ie you have #define

in your .c file and use the macro in the header and not in another way around), you don't have this Problems. Poul-Henning Kamp, of FreeBSD fame, explains very well how to do this here .

+1


source







All Articles