Search code examples
cgetter-setter

Implicit casting to union type?


Let's say we have the following parameters describing a person: name (string) and age (unsigned int). I want to write a universal setter API function that someone can call to set either the name or the age of a specific person. The reason for that will be explained later below.

What I did is define an enum type of person parameter names:

typedef enum person_param_name
{
    NAME,
    AGE,
} person_param_name_t;

And also a union type for person parameter values:

typedef union person_param_val
{
    char* name;
    unsigned int age;
} person_param_val_t;

Now, the function can look like this:

int set_person_param(person_param_name_t param_name, person_param_val_t param_val)
{
    int ret = 0;

    switch (param_name)
    {
        case NAME:
            g_person_name = param_val.name;
            break;
        case AGE:
            g_person_age = param_val.age;
            break;
        default:
            ret = -1;
            break;
    }

    return ret;
}

The problem with this approach is that one can't simply call the setter function like this (compiler throws warning):

set_person_param(NAME, "Alex");
set_person_param(AGE, 5);

But they have to explicitly cast the param value to person_param_val_t type, like this:

set_person_param(NAME, (person_param_val_t)"Alex");
set_person_param(AGE, (person_param_val_t )5);

The reason I want the universal setter function is because in the real program, I have a lot more parameters (close to 100) and I would need to write many (very similar) setter functions which would take a lot more lines of code.

Is there a better approach to this?


Solution

  • If you change the union so that the field names are identical to the enum constants:

    typedef union person_param_val
    {
        char* NAME;
        unsigned int AGE;
    } person_param_val_t;
    

    Then you can create a macro which will pass a properly initialized compound literal:

    #define set_person_param_ext(k,v) \
            set_person_param(k, (person_param_val_t){.k=v})
    

    So then this:

    set_person_param_ext(NAME, "Alex");
    set_person_param_ext(AGE, 5);
    

    Will expand to this:

    set_person_param(NAME, (person_param_val_t){.NAME="Alex"});
    set_person_param(AGE, (person_param_val_t){.AGE=5});