mardi 13 octobre 2015

Conditionally initialize a struct field - only if it exists in that struct

Working on something like a unit testing framework for a very simple API:

extern "C" void execute_command(int cmd, void *params);

Which casts the params to the appropriate struct based on the cmd argument. I can't change that interface, nor can I modify the header which specifies the commands and the different param structures (which are all POD).

I do have access to an array of something like:

{ 0 /*cmd number*/, "PARAM_STRUCT_FOR_CMD_0", sizeof(PARAM_STRUCT_FOR_CMD_0) }

These param structs have some common properties. For example, many of them have a field like void *pObject;, though it is not always the same offset. To illustrate, suppose there are three structures:

struct {
    void *pObject;
    int someData;
} PARAM_STRUCT_FOR_CMD_0;
struct {
    float someFloatData;
    void *pObject;
} PARAM_STRUCT_FOR_CMD_1;
struct {
    float someFloatData;
    void *pAnotherObject;
} PARAM_STRUCT_FOR_CMD_2;

These two pObject fields represent the same thing, while pAnotherObject is unrelated.

Now, on to what I actually want: I'd like to cast a void* to some struct, based on cmd, and set its pObject field, if it exists in that struct. Ideally, I'd be able to do something like:

void *pGlobalObject;
void execcmd(int cmd)
{
    static uint8_t params[MAX_SIZE_OF_PARAM_STRUCT];

    memset(params, 0, MAX_SIZE_OF_PARAM_STRUCT);
    INIT_STRUCT_IF_POSSIBLE(cmd, (void*)params);
    execute_command(cmd, params);
}

Where INIT_STRUCT_IF_POSSIBLE could be something like:

#define INIT_STRUCT_IF_POSSIBLE(cmd, str) \
do {  \
    switch (cmd)  \
    {  \
        case 0: static_cast<PARAM_STRUCT_FOR_CMD_0*>(str)->pObject = pGlobalObject; break;  \
        case 1: static_cast<PARAM_STRUCT_FOR_CMD_1*>(str)->pObject = pGlobalObject; break;  \
        case 2: /* nothing, no pObject field */ break;  \
    }  \
} while (0)

except that isn't really scalable. I have ~1000 possible commands, and let's say 5 fields which I'd like to set (no struct has all 5), and new commands can be added, so I'd like to avoid manually changing this.

The obvious solution is an extra build step that parses all the structs, and creates their initializers. Adding this extra build step is a lot of pain though, due to how the project is structured, so I'm hoping for a pure C++ solution.

If there's a way to generate the initializers using the C preprocessor, I'm all for it. If it can somehow be done using templates, just as good. I have boost and C++11 available, if it helps.

One thing that would solve this is the designated initializers, such as STR x = {.pObject = pGlobalObject; };. Unfortunately, they cause an error when the field is not available. Any way to just ignore nonexistent fields? (Yes, I know they are C only, not C++, but I can switch to C if needed)





Aucun commentaire:

Enregistrer un commentaire