为什么 C 泛型使用 void * 而不是宏?

问题描述 投票:0回答:1

据我了解,在 C 中实现通用数据类型的标准方法是使用 void 指针。然而,另一种方法是使用宏。这是使用宏的通用“Option”类型的示例实现:

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#define OPTION(T)                                                              \
  struct option_##T {                                                          \
    bool exists;                                                               \
    T data;                                                                    \
  };                                                                           \
  typedef struct option_##T option_##T;                                        \
  static inline bool is_some_##T(option_##T x) { return x.exists; }            \
  static inline bool is_none_##T(option_##T x) { return !x.exists; }

#define is_some(x) _Generic((x), option_int: is_some_int, option_char: is_some_char)(x)
#define is_none(x) _Generic((x), option_int: is_none_int, option_char: is_none_char)(x)

OPTION(int);
OPTION(char);

int main(int argc, char *argv[]) {
  option_int x = {.exists = true, .data=3};
  option_char y = {.exists = true, .data='a'};
  
  printf("x is some: %d\n", is_some(x));
  printf("y is some: %d\n", is_some(y));
  
  return 0;
}

另一种方法是使用 void * 进行数据输入。然而,这会增加一个额外的间接层(同时可能节省二进制大小)。 void * 方法更常见有什么原因吗?

c generics
1个回答
0
投票

因为

_Generic
是一个相对较新的功能。 “老派”的方法总是使用空指针,或者通过创建一个与示例类似的结构,但通常使用枚举来标记所表示的类型。或者通过特定类型的回调,如众所周知的
bsearch
/
qsort
.

至于

_Generic
和即将标准化的
typeof
,有很多不同的使用方式。实际上也不需要包装器结构/枚举。例如,您可以将它们用作“穷人的模板” - 也许不推荐这样做,但功能非常强大并且类型安全:

#include <stdio.h>

#define TYPES_SUPPORTED(X) \
  X(int,  %d)              \
  X(char, %c)              \

#define PRINT(type, fmt)   \
void type##_print (type t) \
{                          \
  printf(#fmt "\n", t);    \
}
TYPES_SUPPORTED(PRINT)

#define GENERIC_PRINT(type, fmt) ,type: type##_print
#define print(x) \
  _Generic((x),  \
  default:0      \
  TYPES_SUPPORTED(GENERIC_PRINT) )(x)

int main() 
{
  int a = 123;
  char c = 'A';
  print(a);
  print(c);
}

这种利用“X 宏”的方式意味着您可以将支持的所有类型显示到列表中,无需像示例中那样调用单个宏。相反,您为每个用例创建一个宏,然后调用 X 宏列表。

因此,此示例创建一个函数

int_print(int t)
和一个
char_print(char t)
,并使用正确的格式说明符打印参数。

然后,我们可以从类型通用

print

 宏依次调用这些函数,其中 
_Generic
 关联列表也已烘焙到 X 宏列表中,从而减少了键入每个条件的需要。这里的邪恶技巧是先放置 
default:
,然后用 
,
 开始每个宏扩展,因为 
_Generic
 与数组/枚举声明不同,初始化列表等不喜欢尾随逗号。

© www.soinside.com 2019 - 2024. All rights reserved.