- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我对 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 之类的头文件,每个文件都包含一组函数原型(prototype)和一个 _Generic。这是可行的,但并不出色。我不喜欢将程序中每种类型的列表集中在一个地方的要求。
我希望能够在头文件中定义类型 foo 以及函数 write_foo,并以某种方式让客户端代码能够调用“函数”write。默认值看起来像一个 vector ,通过它可以实现这一点。
我可以在此站点上找到的最接近的匹配项是 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)
在此 header 的下方,我想要一个处理 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“函数”的方法,即不从它将映射的所有类型的列表开始吗?谢谢。
最佳答案
跨多个不相关文件的类型泛型函数的需求表明程序设计很差。
要么这些文件是相关的,并且应该共享一个共同的父类(“抽象基类”),然后可以在其中声明泛型宏和函数声明。
或者它们不相关,但出于某种原因共享一些通用方法,在这种情况下,您需要发明一个通用的通用抽象层接口(interface),然后它们才能实现。您应该始终首先考虑系统级别的程序设计。
这个答案没有使用_Generic
,而是提出了一个完全不同的程序设计。
以注释中的示例为例,bool equal(T lhs, T rhs)
。也就是上面两种情况的后一种情况,多个模块共享的公共(public)接口(interface)。首先要注意的是,这是一个仿函数,一个可以依次被通用算法(例如搜索/排序算法)使用的函数。 C 标准建议应该如何编写仿函数:
int compare (const void* p1, const void* p2)
这是标准函数 bsearch
和 qsort
使用的格式。除非你有充分的理由,否则你不应该偏离这种格式,因为如果你不这样做,你将免费进行搜索和排序。此外,这种形式的优点是可以在同一个函数中进行更小、更大和相等的检查。
在 C 中为此类函数实现通用接口(interface)的经典 C 方法是包含此宏的 header :
接口(interface)标题:
#define compare(type, x, y) (compare_ ## type(x, y))
实现 header 的模块:
// int.c
int compare_int (const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
来电者:
if( compare(int, a, b) == 0 )
{
// equal
}
这具有抽象的优点:接口(interface)头文件不需要知道所有使用的类型。缺点是根本没有类型安全。
(但这是 C,你永远不会通过编译器获得 100% 的类型安全。如果它是一个大问题,请使用静态分析。)
在 C11 中,您可以通过引入 _Generic
宏来稍微提高类型安全性。但这有一个大问题:该宏必须提前了解所有现有类型,因此您不能将其放在抽象接口(interface) header 中。相反,它应该不在一个公共(public)头文件中,因为那样您将在使用该头文件的每个单独的、不相关的模块之间创建紧密耦合。您可以在调用应用程序中创建这样一个宏,不是为了定义接口(interface),而是为了确保类型安全。
您可以改为通过继承抽象基类来强制实现接口(interface):
// 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;
继承接口(interface)的模块在创建对象时将比较函数指针设置为指向特定的比较函数。 data
是模块私有(private)的,可以是任何东西。假设我们创建一个名为“xy”的模块继承了上述接口(interface):
//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 宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33905692/
给定以下源代码: #include #include #include static inline void closedirp(DIR **p) { if (*p) c
在 C11 中,我可以创建一个原型(prototype)如下所示的函数: void myVaFunc(const char* const conv, ...); 我可以这样运行它: myVaFunc(
我正在阅读这个 C11 的 _Generic维基百科上的例子: #define cbrt(X) _Generic((X), long double: cbrtl, \
我需要在 C 中使用 _Generic,以便能够根据变量类型返回专门的值。 const char *encode_char_ptr(char *x) { return (x ? x : "NU
所以我正在尝试实现“通用打印宏”: #include #include #define CHECK(x) printf(#x " =" \ _Generic((x), float:
我试图学习如何使用“新的”C11 泛型表达式,但我遇到了困难。 考虑以下代码: #include #include #define test(X, Y, c) \ _Generic((X)
如何向 c11 _Generic Functions 添加额外类型? 您是否必须#undef/re-#define 它?(如果是这样,下面的工作是否可行)还是有更好的方法? #define to_st
我对 C11 的 _Generic 机制感到高兴 - 类型切换是我在 C++ 中怀念的东西。然而,事实证明它很难编写。 例如,给定函数: bool write_int(int); bool write
有没有办法在同一表达式中多次使用 _Generic 关键字来创建单个字符串文字? 我正在寻找的是一种生成单一格式字符串以传递给 printf 的方法,所有转换说明符都适合适当的类型。 写this时答案
我有一个这样的模板: template.h ---------- // Declare a function "func_type()" void JOIN(func_, T)(T t) { retu
我正在使用 C 的在线编译器和调试器。此网页使用 GCC 5.4.1 C99。当我测试这段代码时, https://www.onlinegdb.com/online_c_compiler #inclu
在下面的代码中: struct Person { char* name; int age; }; struct Book { char* title; char* au
我正在设置一些大宽度整数类型,因此我大量使用宏来使这些类型尽可能像基本整数类型一样可用。我一直遇到的一个问题是,如果我在宏扩展中大量使用 _Generic 表达式,而不是最小化 _Generic 使用
我想在C中组合两种类型来调用函数(例如具有不同列和行的乘法 vector 和矩阵): #define CC_FIRST(a) _Generic((a), int: int8d) #def
我有兴趣编写一个类型验证宏,它只在类型不是 int/short/long 或指针时发出警告。 我遇到的问题是指针可以是任何类型的指针。 #define INT_OR_POINTER_AS_UINTPT
我似乎无法将参数传递给需要不同参数的函数(或传递给实现第一个类型的子集的其他 _Generic 宏)。 #define DEBUG_PRINT(x,...) _Generic((x),
可能我在这里做错了一些非常基本的事情,但我无法弄清楚那可能是什么。我可以解释我的代码片段,但我认为我在这里尝试做的事情非常明显:为我的 DynamicArray 的 dynarr_printf 创建一
我正在尝试在 C11 中使用 _Generic 宏生成重载函数,并且我已经停止了对零参数函数的支持,例如: #define msg(_1) _Generic((_1), char*: msg_stri
使用 gcc.exe(Rev3,由 MSYS2 项目构建)8.2.0。 我试图构建一个宏来自动在两种类型之间进行类型转换,其中两个参数永远不应该是同一类型。我的问题是如果我不包含相同类型的情况,编译器
我想使用 C11 _Generic 关键字根据静态类型填充 union ,如: typedef union { double d; long l; const char*s;
我是一名优秀的程序员,十分优秀!