组合_Generic宏

我对C11的_Generic机制很满意 – 切换类型是我从C ++中遗漏的东西。 然而,certificate难以撰写。

举个例子,给定函数:

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(obj),并且,如果obj是其中一种类型的实例,一切都很好。

然而,一般来说,foo和bar彼此完全无关。 它们在不同的头文件中定义,很少(但偶尔)在单个源文件中一起使用。 然后应该在哪里写宏扩展到_Generic?

目前,我正在积累一些名为write.h,equal.h,copy.h,move.h的头文件,每个文件包含一组函数原型和一个_Generic。 这是可行的,但并不精彩。 我不喜欢在一个地方收集程序中每种类型的列表的要求。

我希望能够在头文件中定义类型foo,以及函数write_foo,并且以某种方式使客户端代码能够调用’function’。 默认看起来像一个矢量,通过它可以实现这一点。

我在这个网站上找到的最接近的匹配是c11generics添加类型 ,它有一个部分解决方案,但是我不太了解如何组合各种宏。

让我们说,在定义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’函数’,即没有从它将映射的所有类型的列表开始? 谢谢。

对多个不相关文件的类型generics函数的需求表明程序设计很差。

这些文件是相关的,并且应该共享一个公共父级(“抽象基类”),然后可以声明类型通用宏和函数声明。

或者它们是无关的,但无论出于何种原因共享一些常用的方法,在这种情况下,您需要发明一个通用的通用抽象层接口,然后他们可以实现它们。 您应该始终首先考虑系统级别的程序设计。

这个答案不使用_Generic ,但完全提出了不同的程序设计。


以评论为例, bool equal(T lhs, T rhs) 。 这是上述两种情况中的后一种情况,即多个模块共享的通用接口。 首先要注意的是,这是一个函子 ,一个函数可以依次用于搜索/排序算法等通用算法。 C标准建议最好如何编写仿函数:

 int compare (const void* p1, const void* p2) 

这是标准函数bsearchqsort使用的格式。 除非你有充分的理由,否则你不应该偏离这种格式,因为如果你不这样做,你将免费搜索和排序。 此外,这种forms的优点是可以在同一function中进行更少,更大和相等的检查。

在C中实现此类函数的通用接口的经典C方法是包含此宏的标头:

接口头:

 #define compare(type, x, y) (compare_ ## type(x, y)) 

实现标头的模块:

 // int.c int compare_int (const void* p1, const void* p2) { return *(int*)p1 - *(int*)p2; } 

呼叫者:

 if( compare(int, a, b) == 0 ) { // equal } 

这具有抽象的优点:接口头文件不需要知道所有使用的类型。 缺点是没有任何类型的安全性。

(但这是C,你永远不会通过编译器获得100%的类型安全性。如果它是一个大问题,请使用静态分析。)

使用C11,您可以通过引入_Generic宏来提高类型安全性。 但是有一个很大的问题:宏必须提前知道所有现有类型,所以你不能把它放在一个抽象的接口头中。 相反,它应该在一个公共标题中,因为那样你将使用该标题在每个单独的,不相关的模块之间创建紧密耦合。 您可以在调用应用程序中创建这样的宏,而不是定义接口,但要确保类型安全。


你可以做的是通过inheritance抽象基类来强制实现接口:

 // interface.h typedef int compare_t (const void* p1, const void* p2); typedef struct data_t data_t; // incomplete type typedef struct { compare_t* compare; data_t* data; } interface_t; 

在创建对象时,inheritance接口的模块将比较函数指针设置为指向特定比较函数。 data是模块专用的,可以是任何东西。 假设我们创建了一个名为“xy”的模块,它inheritance了上面的接口:

 //xy.c struct data_t { int x; int y; }; static int compare_xy (const void* p1, const void* p2) { // compare an xy object in some meaningful way } void xy_create (interface_t* inter, int x, int y) { inter->data = malloc(sizeof(data_t)); assert(inter->data != NULL); inter->compare = compare_xy; inter->data->x = x; inter->data->y = y; } 

然后,调用者可以使用通用interface_t并调用compare成员。 我们已经实现了多态性,因为随后将调用特定于类型的比较函数。

松散地根据Leushenko对多参数generics的回答,我提出了以下可怕的解决方案。 它要求参数将通过指针传递,并且涉及的样板非常糟糕。 它以编程方式运行,允许函数返回一个值。

 // foo.h #ifndef FOO #define FOO #include  #include  struct foo { int a; }; static inline int write_foo(struct foo* f) { (void)f; return printf("Writing foo\n"); } #if !defined(WRITE_1) #define WRITE_1 #define WRITE_PRED_1(x) _Generic((x), struct foo * : true, default : false) #define WRITE_CALL_1(x) \ _Generic((x), struct foo * \ : write_foo((struct foo*)x), default \ : write_foo((struct foo*)0)) #elif !defined(WRITE_2) #define WRITE_2 #define WRITE_PRED_2(x) _Generic((x), struct foo * : true, default : false) #define WRITE_CALL_2(x) \ _Generic((x), struct foo * \ : write_foo((struct foo*)x), default \ : write_foo((struct foo*)0)) #elif !defined(WRITE_3) #define WRITE_3 #define WRITE_PRED_3(x) _Generic((x), struct foo * : true, default : false) #define WRITE_CALL_3(x) \ _Generic((x), struct foo * \ : write_foo((struct foo*)x), default \ : write_foo((struct foo*)0)) #endif #endif // bar.h #ifndef BAR #define BAR #include  #include  struct bar { int a; }; static inline int write_bar(struct bar* b) { (void)b; return printf("Writing bar\n"); } #if !defined(WRITE_1) #define WRITE_1 #define WRITE_PRED_1(x) _Generic((x), struct bar * : true, default : false) #define WRITE_CALL_1(x) \ _Generic((x), struct bar * \ : write_bar((struct bar*)x), default \ : write_bar((struct bar*)0)) #elif !defined(WRITE_2) #define WRITE_2 #define WRITE_PRED_2(x) _Generic((x), struct bar * : true, default : false) #define WRITE_CALL_2(x) \ _Generic((x), struct bar * \ : write_bar((struct bar*)x), default \ : write_bar((struct bar*)0)) #elif !defined(WRITE_3) #define WRITE_3 #define WRITE_PRED_3(x) _Generic((x), struct bar * : true, default : false) #define WRITE_CALL_3(x) \ _Generic((x), struct bar * \ : write_bar((struct bar*)x), default \ : write_bar((struct bar*)0)) #endif #endif // write.h #ifndef WRITE #define WRITE #if defined(WRITE_3) #define write(x) \ WRITE_PRED_1(x) ? WRITE_CALL_1(x) : WRITE_PRED_2(x) ? WRITE_CALL_2(x) \ : WRITE_CALL_3(x) #elif defined(WRITE_2) #define write(x) WRITE_PRED_1(x) ? WRITE_CALL_1(x) : WRITE_CALL_2(x) #elif defined(WRITE_1) #define write(x) WRITE_CALL_1(x) #else #error "Write not defined" #endif #endif // main.c #include "foo.h" #include "bar.h" #include "write.h" int main() { struct foo f; struct bar b; int fi = write(&f); int bi = write(&b); return fi + bi; } 

我真的希望有比这更好的方法。