大佬教程收集整理的这篇文章主要介绍了组合_Generic宏,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
举个例子,给定函数:
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,并且以某种方式使客户端代码能够调用’function’.默认看起来像一个矢量,通过它可以实现这一点.
我在这个网站上找到的最接近的匹配是c11 generic adding types,它有一个部分解决方案,但是我不太了解如何组合各种宏.
让我们说,在定义write_bar的头文件中的某个地方,我们有一个现有的宏定义:
#define write(X) _Generic((X),bar: write_bar,default: some_magic_herE)(X)
#define write_impl(X) _Generic((X),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,但完全提出了不同的程序设计.
以评论为例,bool相等(T lhs,T rhs).这是上述两种情况中的后一种情况,即多个模块共享的通用接口.首先要注意的是,这是一个仿函数,一个可以通过搜索/排序算法等通用算法依次使用的函数. C标准建议最好如何编写仿函数:
int compare (const void* p1,const void* p2)
这是标准函数bsearch和qsort使用的格式.除非你有充分的理由,否则你不应该偏离这种格式,因为如果你不这样做,你会得到搜索和免费整理.此外,这种形式的优点是在同一功能中进行更少,更大和相等的检查.
接口头:
#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宏来提高类型安全性.但是有一个很大的问题:宏必须提前知道所有现有类型,所以你不能把它放在一个抽象的接口头中.相反,它不应该在一个公共标题中,因为那样你将使用该标题在每个单独的,不相关的模块之间创建紧密耦合.您可以在调用应用程序中创建这样的宏,而不是定义接口,但要确保类型安全.
你可以做的是通过继承抽象基类来强制实现接口:
// 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;
在创建对象时,继承接口的模块将比较函数指针设置为指向特定比较函数.数据是模块专用的,可以是任何东西.假设我们创建了一个名为“xy”的模块,它继承了上面的接口:
//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成员.我们已经实现了多态性,因为随后将调用特定于类型的比较函数.
以上是大佬教程为你收集整理的组合_Generic宏全部内容,希望文章能够帮你解决组合_Generic宏所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。