有人可以解释这个宏#define g_once_init_enter(location)对我来说是个glib吗?

时间:2018-02-23 07:03:36

标签: c glib

当我读到这个宏时,我有点困惑: #define g_once_init_enter(location),这是在glib库中定义的。

#define g_once_init_enter(location)                            \
  (G_GNUC_EXTENSION({                                          \
  G_STATIC_ASSERT(sizeof *(location) == sizeof(gpointer));     \
  (void) (0 ? (gpointer) *(location) : 0);                     \
  (!g_atomic_pointer_get (location) &&                         \
  g_once_init_enter (location));                               \
}))

这一行有什么影响:(void) (0 ? (gpointer) *(location) : 0); 而最后一行又是g_once_init_enter(location),它是一个死循环吗? 谢谢你的回复。

2 个答案:

答案 0 :(得分:2)

这是一些非常难看的代码。逐行:

  • G_GNUC_EXTENSION显然与__extension__相同,这是隐藏地毯下方不良代码的肮脏技巧。它告诉gcc编译器将非标准代码视为正确的C.
  • G_STATIC_ASSERT(sizeof *(location) == sizeof(gpointer));是一个前C11静态断言,如果指向的数据与类型gpointer的大小不同,它将扩展为某种形式的神秘编译器错误。这是尝试获得一点点类型安全的尝试。
  • (void) (0 ? (gpointer) *(location) : 0);是一种拙劣的尝试,通过编写一条从未执行但包含强制转换的行来实现类型安全。值得注意的是,C中的几乎任何类型都可以转换为几乎任何其他类型,而不会引起编译器错误,因此这条线路根本没有实现。
  • 其余的是两个函数调用,g_once_init_enter仅在g_atomic_pointer_get返回0 / NULL的情况下执行。

总的来说,宏尝试在C中实现类型安全,但是不能完全管理。主要是因为它不可能完成任务,至少在C11之前。

值得注意的是,有一个名为gpointer的类型是有问题的,因为它意味着该类型根本不是指针,或者它是隐藏在typedef后面的指针。在任何一种情况下,都是非常糟糕的做法。

在现代C中你可能会用这个代替这整个混乱:

#define g_once_init_enter(location) \
  _Generic(*(location), gpointer :  \
  !g_atomic_pointer_get (location) && g_once_init_enter (location) )

答案 1 :(得分:1)

这看起来像是对我的隐式类型检查。

宏显然希望传递gpointer的地址(无论是什么),但是宏没有参数类型,所以它不能这么说。

相反,它断言sizeof *(location)sizeof(gpointer)相同,后者验证location可以被解除引用,并且它指向的内容具有正确的大小(否则G_STATIC_ASSERT行不会编译)。

然后通过编译强制转换(location),确保gpointer指向的任何内容都可以转换为(gpointer) *(location)。这一行没有其他效果(并且在运行时永远不会达到强制转换);如果演员阵容无效,那只会让编译器抱怨。

宏不会递归扩展。最后一行g_once_init_enter(location)保持原样,因此必须在某处可以调用实际的g_once_init_enter函数。