简体   繁体   English

隐式转换为联合类型?

[英]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.我想写一个通用的设置器 API function,有人可以调用它来设置特定人的姓名或年龄。 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:还有一个用于 person 参数值的联合类型:

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

Now, the function can look like this:现在,function 看起来像这样:

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):这种方法的问题在于不能像这样简单地调用 setter function(编译器会抛出警告):

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:但是他们必须明确地将参数值转换为person_param_val_t类型,如下所示:

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.我想要通用 setter function 的原因是因为在实际程序中,我有更多的参数(接近 100 个)并且我需要编写许多(非常相似的)setter 函数,这将需要更多的代码行。

Is there a better approach to this?有更好的方法吗?

I could see:我能看见:

typedef enum person_param_name {
    NAME,
    AGE,
} person_param_name_t;

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

person_param_val_t person_param_val_init_charp(char *name) {
    return (person_param_val_t){.name=name};
}
person_param_val_t person_param_val_init_u(unsigned age) {
    return (person_param_val_t){.age=age};
}
#define MAKE_PERSON_PARAM_VAL(x)  _Generic((x) \
    , unsigned: person_param_val_init_u \
    , char *:person_param_val_init_charp \
    )(x)

int set_person_param(person_param_name_t param_name, person_param_val_t param_val);

#define set_person_param(a, b) \
    set_person_param(a, MAKE_PERSON_PARAM_VAL(b))
    
int main() {
    set_person_param(NAME, "Alex");
    set_person_param(AGE, 5u);
}

With GCC with extension, you will get away with just:使用带有扩展名的 GCC,您将摆脱困境:

#define set_person_param(a, b) \
     set_person_param(a, (person_param_val_t)(b))

But I would not write such code.但我不会写这样的代码。 This is C. In C, you would write it all explicitly.这是 C。在 C 中,您将显式地全部写入。 I do not see a value in person_param_name .我没有在person_param_name中看到值。 You still have to enumerate all types explicitly inside set_person_param .仍然必须在set_person_param中明确枚举所有类型。 I would just write set_person_param_age(unsigned age) and set_person_param_name(char *name) explicitly.我会明确地写set_person_param_age(unsigned age)set_person_param_name(char *name) If not, I would consider rethinking the whole approach, as most probably you want to implement virtual function. I would advise, strongly consider not writing an interface with endless number of cases in enums, because you might end up with this .如果没有,我会考虑重新考虑整个方法,因为您很可能想实现虚拟 function。我强烈建议您不要在枚举中编写一个包含无数个案例的接口,因为您最终可能会得到这个. Instead, create objects with a pointer to the interface stored with a vtable.相反,使用指向存储在 vtable 中的接口的指针创建对象。

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});

You do not need any magic.你不需要任何魔法。 This macro is enough这个宏就够了

#define set_person_param(param, val)  g_person_##param.param = (val)

And this sample function:而这个样本 function:

int foo(void)
{
    person_param_val_t g_person_name, g_person_age;

    set_person_param(name, "Alex");
    set_person_param(age, 5);
}

will be preprocessed to:将被预处理为:

int foo(void)
{
    person_param_val_t g_person_name, g_person_age;

    g_person_name.name = "Alex";
    g_person_age.age = 5;
}

As I understand it was something you wanted to archive.据我了解,这是您想存档的内容。 Your enum type is not needed.不需要您的枚举类型。

If you want val to be the same union type then:如果您希望val是相同的联合类型,则:

#define set_person_param(param, val)  g_person_##param = (val)

example:例子:

    set_person_param(name, (person_param_val_t){.name="Alex"});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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