通用函数宏以及如何抑制特定GCC警告:“条件表达式中的指针类型不匹配”

时间:2014-12-24 17:11:10

标签: c pointers gcc macros c11

在某人立即将此标记为副本之前,请允许我说我花了几个小时寻找答案(并阅读许多类似的S / O问题)

情况就是这样:我正在玩_Generic并尝试实现一个字典结构,该结构在检索时自动转换。让我解释一下(如果你不在乎,请跳到粗体标题)。从我看到的,具有字典结构的首选方法,其中所有值都属于同一字段涉及void指针,这需要用户在检索时进行转换;这是一个例子:

struct C99_Dict *d = new_C99_Dict(); /* Creates a pointer to an empty dict
                                        d: {} */
int i = 7;
put_in_c99_dict(d,"key",i); /* d: {"key": (void *)&i} */
...
// BAD: Will cast to i's address
int j = get_from_c99_dict(d,"key");
// BAD: Dereferencing void pointer
int k = *(get_from_c99_dict(d,"key"));
// GOOD: Will set l equal to 7
int l = *(int *)get_from_c99_dict(d,"key");

正如人们可能想象的那样,经过一段时间(特别是一旦struct *被抛入混合中......虽然在我当前的项目中没有改变)你的代码最终看起来像Lisp教科书中的东西。 / p>

然而,使用_Generic,我已经设法找到一种方法来制作一个更易于使用的字典,该字典以这样的方式自动播放

int j = get_from_c11_dict(d,"key");

变得完全有效并且按照人们的预期工作(对于内置...结构仍需要手动转换)。根据输入类型更改put_in_c11_dict的行为非常简单,因为_Generic关键字可以完成所有繁重工作。然而,难以理解的是,在 out 的路上投射的概念。这是因为,为了明确定义字典struct,其值必须是一致的类型(例如void*,正如我已实现的那样)。但是,问题在于插入函数处理完给定输入后类型信息会丢失。

我最初(失败)尝试解决此问题的方法是创建以下形式的字典结构:

typedef struct _dict_mem_struct{
  union {
    _Bool(*bool_get)(_dict_mem_struct*);
    char(*char_get)(_dict_mem_struct*);
    ...
    char *(*char_point_get)(_dict_mem_struct*);
    void *(*void_point_get)(_dict_mem_struct*);
  } get;
  void *value;
} _dict_mem_t;

希望(尽管可能是愚蠢的)能够在_get辅助宏定义中执行以下操作:

#define _get(mem_struct) _Generic((mem_struct.get) ... )

不幸的是,我从gdb那里了解到mem_struct.get的类型为union,所以它又回到了绘图板。最终,我得到了一些有用的东西。首先,我在包含原始类型的成员结构中添加了char*字段。那么我真正需要的是一个内联的switch语句,因为我之前没有指出函数签名是什么。所以,这是我做的可怕的事情(技术上是无效的C?也许。可能。我不知道。但是,GCC编译它并且它有效,所以我很高兴。)

#define IS_PFX(val,pfx) (!strcmp(val->pfx, pfx))
#define _get(valstruct) (IS_PFX(valstruct,"bool") ? boolval(valstruct) : IS_PFX(valstruct,"char") ? charval(valstruct) : ... : valstruct)
是的,我知道;我可能会为此而痛苦。所以,有了......

这是我的实际问题:当我编译它时,它可以工作,但是gcc对我非常不满。它给了我一堆错误,比如

dict.c:203:75: warning: pointer type mismatch in conditional expression
 #define PAIR(valstruct,str,fn,rst) (IS_PFX(valstruct,str) ? fn(valstruct) : rst)

从我可以收集的内容来看,这意味着gcc对这些功能都属于不同类型感到不安。尽管如此,如前所述,代码可以工作,所以我想告诉gcc在其中放置一个袜子,特别是那些警告。问题是,当我运行gcc -fdiagnostics-show-option时,这些警告行后面没有-W...标志。此外,我已经阅读了gcc警告标志页面,没有什么对我来说显而易见,我可以使用它来抑制这些。最后,添加行#pragma GCC diagnostic ignored "-Wall"#pragma GCC diagnostic ignored "-Wextra"后,警告仍然不会消失。我怎么能摆脱这些?我可以解决的另一个解决方案是以某种方式关闭特定文件的所有警告,因为我不想要它们的原因是我可以将其集成到其他项目而不会头疼。

感谢您提供的所有帮助。顺便说一句,如果有更好的方法来做所有这些,那么让我知道。无论如何,我想我曾经做过一些git repo,因为我认为它会有用(如果有的话,我会用链接更新这篇文章)。

1 个答案:

答案 0 :(得分:4)

gcc可能是对的,你在三元表达式中混合不同的指针类型。所以你的设计很可能是错的。请记住,_Generic不能创造奇迹,C中的类型系统保持静态,在编译时确定。它只能处理您在第一个表达式中传递给它的类型信息。

如果您将类型信息丢弃到void*,请将指针存储在某处并尝试稍后检索,根据定义_Generic无法帮助您。检索的上下文不再具有类型信息,可能会调用来自不同位置的指针。

因此,特别是对于字典结构,C在检索端永远不会知道原始指针的类型。如果你想保留这些信息,你必须自己做,然后将这些信息与指针一起存储。

顺便说一下,你的问题标题已经错了:C中没有通用函数这样的东西。有类型泛型函数式宏。