【问题标题】:How do you use _Generic with structs that are typedef-ed in C?您如何将 _Generic 与 C 中 typedef-ed 的结构一起使用?
【发布时间】:2021-09-27 13:39:32
【问题描述】:

我是 C 新手,目前正在通过在 C11 中使用 _Generic 来探索函数重载的概念。

this SO post 中,@Lundin 提供了以下代码 sn-p,我运行起来没有任何问题:

#include <stdio.h>

void func_int (int x) { printf("%d\n", x); }
void func_char (char ch) { printf("%c\n", ch); }

#define func(param)          \
  _Generic((param),          \
    int:  func_int(param),   \
    char: func_char(param)); \

int main() 
{
  func(1);
  func((char){'A'});
}

@Lundin 还提到了以下内容:

一个更好的方法是用一个单一的函数接口 struct 参数,可以调整为包含所有必要的 参数。有了这样的界面就可以使用上面的简单方法 基于_Generic。类型安全且可维护。

所以我决定将上面的示例扩展到structs 以查看它的实际效果。

以下是我的尝试:

#include <stdlib.h>
#include <stdio.h>

typedef struct args_int Args_int;
typedef struct args_char Args_char;

void func_int(Args_int args);
void func_char(Args_char args);

struct args_int {
    int val;
};
struct args_char {
    char val;
};

#define func(param)\
    _Generic((param),\
        Args_int: func_int(param),\
        Args_char: func_char(param));


void func_int (Args_int args) {
    printf("%d\n", args.val);
}

void func_char (Args_char args) {
    printf("%c\n", args.val);
}

int main(void) {
    Args_char args = {0};
    args.val = 'A';
    func(args);
}

但是,不幸的是,我收到以下编译错误,它抱怨我在编译器期望 Args_int 时传入了 Args_char。显然,我的意图是传递一个Args_char,而我的期望是调用func_char

struct_args_by_val_executable.c:35:10: error: passing 'Args_char' (aka 'struct args_char') to parameter of incompatible type 'Args_int' (aka 'struct args_int')
    func(args);
         ^~~~
struct_args_by_val_executable.c:20:28: note: expanded from macro 'func'
        Args_int: func_int(param),\
                           ^~~~~
struct_args_by_val_executable.c:24:25: note: passing argument to parameter 'args' here
void func_int (Args_int args) {
                        ^
1 error generated.

为什么我的示例没有按预期工作?这里的解决方法是什么?

在相关说明中,我设法让“指向结构的指针”版本正常工作,如下所示。但是,鉴于上述编译错误,我觉得这可能是侥幸?

#include <stdio.h>

typedef struct args_int Args_int;
typedef struct args_char Args_char;

void func_int(Args_int *args);
void func_char(Args_char *args);

struct args_int {
    int val;
};
struct args_char {
    char val;
};

#define func(param)\
    _Generic((param),\
        Args_int*: func_int(param),\
        Args_char*: func_char(param));


void func_int (Args_int *args) {
    printf("%d\n", args->val);
}

void func_char (Args_char *args) {
    printf("%c\n", args->val);
}

int main(void) {
    Args_char args = {0};
    args.val = 'A';
    func(&args);
}

上面的输出如预期:

A

【问题讨论】:

  • 嘿,这实际上不是您在那里找到的我的一个非常好的代码示例:) 最好将参数列表包含在 _Generic 子句之外,就像 tstanisl 在他们的回答中刚刚提到的那样。这就是 C 委员会打算如何使用该功能的方式,您可以在标准本身中找到此类示例。
  • 也许我应该去编辑那个帖子,因为这(可以说)是一个错误。该示例编译是因为 charint 在函数调用期间可以相互隐式转换。
  • @Lundin Ohhhhh 我觉得有道理:) 感谢您的澄清! (P.S.你看到这个帖子真是巧合哈哈)
  • 我可能在这个网站上花费了太多时间......无论如何,我现在编辑了那个帖子,所以它不会给将来的任何人带来类似的麻烦:)

标签: c generics struct overloading


【解决方案1】:

问题在于_Generic 中使用的所有 表达式必须有效。 在您的示例宏中,func 扩展为:

int main(void) {
    Args_char args = {0};
    args.val = 'A';
    _Generic(args,
             Args_int: func_int(args),
             Args_char: func_char(args));
}

请注意,将func_int(args) 用于args(即Args_char)会导致错误。

解决方案是使用_Generic 选择一个函数指针,然后对其应用参数。

#define func(param)                       \
    _Generic((param),                     \
             Args_int: func_int,          \
             Args_char: func_char) (param)

【讨论】:

    【解决方案2】:

    只是为了“记录”发布这个:

    这实际上不是你在那里找到的我的一个很好的代码示例!正如@tstanisl 刚刚在他们的回答中提到的那样,将参数列表包含在_Generic 子句之外会更好(我会接受那个作为您问题的答案)。这就是 C 委员会打算使用该功能的方式,您可以在标准本身中找到此类示例,例如 6.5.1:

    #define cbrt(X) _Generic((X),               \
                            long double: cbrtl, \
                            default: cbrt,      \
                            float: cbrtf        \
                            )(X)
    

    您找到的代码 sn-p 很幸运,因为 charint 在调用函数时可以“像通过赋值一样”相互转换。当使用两个不能被隐式转换的不兼容类型时,宏会为错误的类型错误地展开。

    我现在更新了https://stackoverflow.com/a/44633838/584518 以使用它:

    #define func(param)          \
      _Generic((param),          \
        int:  func_int,          \
        char: func_char)(param)  \
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多