【发布时间】:2016-02-27 14:30:37
【问题描述】:
我对 C11 的 _Generic 机制感到高兴 - 切换类型是我在 C++ 中所怀念的。然而,事实证明它很难作曲。
举个例子,给定函数:
bool write_int(int);
bool write_foo(foo);
bool write_bar(bar);
// bool write_unknown is not implemented
然后我就可以写了
#define write(X) _Generic((X), \
int : write_int, \
foo: write_foo, \
bar: write_bar, \
default: write_unknown)(X)
并且,如果我不尝试使用 &write 或将其传递给函数,我可以调用 write(obj) 并且如果 obj 是其中一种类型的实例,那么一切都很好。
但是,通常 foo 和 bar 彼此完全无关。它们在不同的头文件中定义,很少(但偶尔)在单个源文件中一起使用。那么扩展为 _Generic 的宏应该写在哪里呢?
目前,我正在积累诸如 write.h、equal.h、copy.h、move.h 之类的头文件,每个头文件都包含一组函数原型和一个 _Generic。这是可行的,但并不出色。我不喜欢在一个地方收集程序中每种类型的列表的要求。
我希望能够在头文件中定义类型 foo 以及函数 write_foo,并以某种方式让客户端代码能够调用“函数”写入。默认看起来像一个向量,通过它可以实现这一点。
我在这个网站上能找到的最接近的匹配是c11 generic adding types,它有一个部分解决方案,但我还不足以了解如何组合各种宏。
假设在定义 write_bar 的头文件的某处,我们有一个现有的宏定义:
#define write(x) _Generic((x), bar: write_bar, default: some_magic_here)(x)
或者我们可以省略尾随的 (x)
#define write_impl(x) _Generic((x), bar: write_bar, default: some_magic_here)
在这个标题的更下方,我想要一个处理 foo 或 bar 的 write() 版本。我认为它需要在默认情况下调用现有的宏,但我不相信预处理器能够重命名现有的写入宏。如果可以的话,以下方法可以工作:
#ifndef WRITE_3
#define WRITE_3(X) write(x)
#undef write(x)
#define write(x) __Generic((x),foo: write_foo,default: WRITE_3)(x)
刚刚输入的内容我可以看到前进的道路:
// In bar.h
#ifndef WRITE_1
#define WRITE_1(x) __Generic((x), bar: write_bar)
#elif !defined(WRITE_2)
#define WRITE_2(x) __Generic((x), bar: write_bar)
#elif !defined(WRITE_3)
#define WRITE_3(x) __Generic((x), bar: write_bar)
#endif
// In foo.h
#ifndef WRITE_1
#define WRITE_1(x) __Generic((x), foo: write_foo)
#elif !defined(WRITE_2)
#define WRITE_2(x) __Generic((x), foo: write_foo)
#elif !defined(WRITE_3)
#define WRITE_3(x) __Generic((x), foo: write_foo)
#endif
// In write.h, which unfortunately needs to be included after the other two
// but happily they can be included in either order
#ifdef WRITE_2
#define write(x) WRITE_1(x) WRITE_2(x) (x)
#elif
// etc
#endif
但这实际上不起作用,因为当 x 与参数列表不匹配时,我找不到使 WRITE_N(x) 扩展为空的方法。我看到了错误
controlling expression type 'struct foo' not compatible with any generic association type
或者
expected expression // attempting to present an empty default clause
我相信在几个文件之间分配 write() 定义 |我需要解决上述任何一个问题。在默认情况下减少为无的 _Generic 子句将起作用,如果没有任何类型匹配,则将减少为无。
变得更加骇人听闻,如果函数采用指向结构的指针而不是结构的实例,并且我提供 write_void(void*x) {(void)x;} 作为默认选项,那么代码确实可以编译并运行。但是,将写入扩展为
write(x) => write_void(x); write_foo(x); write_void(x);
本身显然很糟糕,而且我真的不想通过指针传递所有内容。
所以 - 任何人都可以看到一种增量定义单个 _Generic '函数' 的方法,即不从它将映射的所有类型的列表开始吗?谢谢。
【问题讨论】:
-
需要跨多个不相关文件的类型泛型函数表明程序设计很差。这些文件要么是相关的,并且应该共享一个公共父级(“抽象基类”),然后可以在其中声明类型泛型宏和函数声明。或者它们不相关,但出于某种原因共享一些通用方法,在这种情况下,您需要发明一个通用的通用抽象层接口,然后它们可以实现。您应该始终首先考虑系统级别的程序设计。
-
假设类型泛型函数是“相等”。通用接口对于所有 T 都是 bool equal(T lhs, T rhs)。你会建议什么共同的父母?我想到的通用抽象层是函数名'equal'。
-
我发布了一个答案,它提出了一种完全不同的方法。