- 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/
如何在 Excel 中编写可以在我将打开的任何 Excel 文档上工作(使用快捷方式运行)的宏? 这可能吗? 最佳答案 您需要将宏添加到 Personal.xlsb 以使它们可用于所有 excel 文
我正在研究 problem #74在4clojure.com,我的解决方案如下: (defn FPS [s] (->> (map read-string (re-seq #"[0-9]+"
我还没有完全理解Clojure 箭头宏thread-first -> 和thread-last 宏->> 之间的区别。在阅读 https://clojure.org/guides/threading_
我想将一些调试输出语句插入到大型 C 代码库中。这些调试输出语句将由编译器选项开关控制。 调试输出语句如下所示: #ifdef DEBUG_FLAG Print(someSymbol) #endif
我正在通过宏将代码注入(inject)到 C++ 类中。有没有办法根据访问修饰符的上下文来做到这一点?有点像 #if (we_are_in_public_context) INJECT_PUBLIC_
这应该与 memoize 类似,但有很大不同。虽然 memoize 应该与纯函数一起使用,但它通常对加速 IO 相关函数很有用。 我正在寻找的函数/宏应该表现得像高阶函数。它产生的功能应该: 第一次调
对于下面的代码: let services: [MyServices] = [ MyService(), #if DEBUG DebugService(), #endi
假设我有以下文本文件 name: John Doe description: My name is John Doe and I'm really good at vim! name: John Do
在创建 Excel 宏方面需要帮助。我有一个 Excel 工作表。Excel 工作表不一致。我打算使它统一和结构化。 例如。 A B C
我正在 excel 中设置一个宏,以便在更新单元格时自动发送电子邮件。是否可以在电子邮件正文中包含单元格的内容?例如,如果单元格 G7 已更新,请在电子邮件中包含单元格 B7 的内容?单元格行将是相同
我创建了一个简单的 Excel 工作表。 这是我的宏代码: Sub MyMacro() Sheets("Sheet1").Select A$ = Cells(1, 1) Msg
在 Excel 的 VB 宏中,如何删除所有出现的以某个字符串开头的单词? 例如: 字符串内容为:xxxx $AUD543.43 yyyy 我想搜索以 $AUD 开头的字符串中的任何内容并删除下一个空
我是 Excel 宏的新手.. 谁能告诉我这个宏是做什么的? Sub People_Add_Document() prow = ActiveCell.row num = Cells(p
我对 Excel 中的 VBA 和宏非常陌生。我有一个非常大的 Excel 电子表格,其中 A 列保存日期。我正在尝试删除值小于某个日期的行,这就是我到现在为止的想法。 Sub DELETEDATE(
我在 Excel 2003 中有一个 VBA 对象,当通过流数据获得某些值时,它会触发三个简单的宏。它运行良好。我想打开一个重复的工作表,但具有不同的流数据,并在各自的工作表上触发宏。它现在可以使用,
下面的宏有什么问题?我只想评估一个选项卡中的一个单元格是否大于另一个选项卡中的另一个单元格。然后消息框: Sub Comhouse() If Worksheets("(2.2) TRA works
需要一个简单的 excel 宏的帮助。我在第 1 列 X1 到 X20 中有数据。我想自动将此信息粘贴到 A 列,然后当我更新 X 列中的数字时,我想将此信息粘贴到 B 列,然后再粘贴到 C 列...
我找到了以下代码,效果很好;但是,我必须手动更改月份,以便它转到第二个工作簿的右侧工作表。由于工作表以月为单位,我怎样才能使其自动更改为当月? Sub AlarmSheet() Dim wkb As
很难说出这里问的是什么。这个问题是模棱两可的、模糊的、不完整的、过于宽泛的或修辞的,无法以目前的形式得到合理的回答。如需帮助澄清这个问题以便重新打开它,visit the help center .
我的公司只使用 MS Office 2003 产品,所以我必须坚持下去。由于我的工作性质,我需要使用很多“复制和粘贴”功能。源数据主要来自网站,我将数据粘贴到 Excel 中的单元格中。问题是剪贴板保
我是一名优秀的程序员,十分优秀!