所以,我想創建一個函式(類似宏),它接受任意數量的不同型別的引數并對它做一些事情。我的意思是,我確實設法讓它作業,但我正在尋找一個更優雅的解決方案(或確保我的方式是它應該看起來的方式)。
函式宏 print(...) 的示例代碼:
#ifndef EVIL_PRINT_H
#define EVIL_PRINT_H
#include <stdio.h>
#define TWENTY_SECOND_ARGUMENT(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, ...) _22
#define COUNT_ARGUMENTS(...) TWENTY_SECOND_ARGUMENT(__VA_ARGS__, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define PRINT_CHAR_EVIL(x) printf("%c", x)
#define PRINT_INT_EVIL(x) printf("%i", x)
#define PRINT_FLOAT_EVIL(x) printf("%f", x)
#define PRINT_DOUBLE_EVIL(x) printf("%d", x)
#define PRINT_PTR_EVIL(x) printf("%p", x)
#define PRINT_STR_EVIL(x) printf("%s", x)
#define PRINT_ONE_EVIL(x, ...) _Generic(x, \
char: PRINT_CHAR_EVIL(x), \
int: PRINT_INT_EVIL(x), \
float: PRINT_FLOAT_EVIL(x), \
double: PRINT_DOUBLE_EVIL(x), \
void *: PRINT_PTR_EVIL(x), \
char const *: PRINT_STR_EVIL(x), \
char *: PRINT_STR_EVIL(x) \
)
#define PRINT_TWO_EVIL(_1, _2, ...) PRINT_ONE_EVIL(_1); PRINT_ONE_EVIL(_2)
...
#define PRINT_TWENTY_ONE_EVIL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) PRINT_TWENTY_EVIL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20); PRINT_ONE_EVIL(_21)
#define print(...) do { \
switch (COUNT_ARGUMENTS(__VA_ARGS__)) { \
default:break; \
case 1: \
PRINT_ONE_EVIL(__VA_ARGS__); \
break; case 2: \
PRINT_TWO_EVIL(__VA_ARGS__, 2); \
... \
break; case 21: \
PRINT_TWENTY_ONE_EVIL(__VA_ARGS__, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21); \
} \
} while(0);
#endif
我對這種方法的問題是它為一個呼叫復制了很多代碼,但我不知道,也許編譯器優化了所有不需要的分支。還有一個限制是它不會接受超過 x(在我的情況下 - 21)數量的引數。添加更多引數沒什么大不了的,但是如果您需要 100 多個引數,檔案大小將會增長。
使用示例:
#include "evil_print.h"
int main(void) {
struct {
int i;
char c;
} v = {.i = 100, .c = 'A'}, o;
o = v;
print(v.i, ", ", o.c);
}
是的,我知道在 c 中很容易,這里就不提那種語言了。
uj5u.com熱心網友回復:
我正在尋找更優雅的解決方案
我最終結束用于https://gitlab.com/Kamcuk/yio/庫的最優雅的解決方案是傳遞取決于引數型別的函式指標陣列。這樣的陣列可以用復合文字在宏擴展內部構造,然后傳遞給調度程式函式。因為您可以va_list通過指向函式的指標傳遞然后對其進行處理,所以您可以在函式指標內執行特定于型別的操作。該設計是為了將前處理器的使用限制在最低限度,盡可能早地分派到函式C端。
#include <stdarg.h>
#include <stdio.h>
#include <limits.h>
#define APPLYFOREACH_1(f,a) f(a)
#define APPLYFOREACH_2(f,a,...) f(a) APPLYFOREACH_1(f,__VA_ARGS__)
#define APPLYFOREACH_3(f,a,...) f(a) APPLYFOREACH_2(f,__VA_ARGS__)
#define APPLYFOREACH_4(f,a,...) f(a) APPLYFOREACH_3(f,__VA_ARGS__)
#define APPLYFOREACH_N(_4,_3,_2,_1,N,...)\
APPLYFOREACH##N
#define APPLYFOREACH(f, ...) \
APPLYFOREACH_N(__VA_ARGS__,_4,_3,_2,_1)(f, ##__VA_ARGS__)
// ---------------------------------------------
// The main logic dispatcher.
typedef int (*lib_print_t)(va_list *va);
int lib_print_in_2(const lib_print_t *printers, va_list *va) {
int result = 0;
for (; *printers != NULL; printers) {
const int tmp = (*printers)(va);
if (tmp < 0) return tmp;
result = tmp;
}
return result;
}
int lib_print_in(const lib_print_t *printers, ...) {
va_list va;
va_start(va, printers);
const int ret = lib_print_in_2(printers, &va);
va_end(va);
return ret;
}
// ---------------------------------------------
// Type specific printers.
int lib_print_int(va_list *va) {
int c = va_arg(*va, int);
return printf("%d", c);
}
int lib_print_char(va_list *va) {
char c = va_arg(*va,
// char is promoted to int.... or is it?
// There are _many_ such cases to handle.
#if CHAR_MAX > INT_MAX
unsigned int
#else
int
#endif
);
return printf("%c", c);
}
int lib_print_charp(va_list *va) {
const char *c = va_arg(*va, char *);
return printf("%s", c);
}
int lib_print_float(va_list *va) {
// but float _is_ promoted to double
float c = va_arg(*va, double);
return printf("%f", c);
}
#define DISPATCH(x) \
_Generic((x) \
, int: lib_print_int \
, char: lib_print_char \
, char*: lib_print_charp \
, const char *: lib_print_charp \
, float: lib_print_float \
/* Note - comma on the end for below */ \
),
// ---------------------------------------------
// Calls lib_print_in with an array of function pointers
// and arguments.
#define lib_print(...) \
lib_print_in( \
(const lib_print_t []){ \
APPLYFOREACH(DISPATCH, __VA_ARGS__) \
NULL \
}, \
##__VA_ARGS__ \
)
// ---------------------------------------------
int main() {
lib_print(1, ", ", 1.0f, (char)'\n');
}
代碼輸出1, 1.000000.
為什么要傳遞函式指標而不是立即呼叫它們?
為了不濫用處理器,它很好地擴展為單個函式呼叫,累積結果,正確處理錯誤,提供格式字串決議等附加功能。但可以肯定的是,如果你不關心這些東西,你可以馬上給他們打電話。您只需_Generic使用 FOREACH 宏即可,僅此而已。
#include <stdio.h>
#define APPLYFOREACH_1(f,a) f(a)
#define APPLYFOREACH_2(f,a,...) f(a) APPLYFOREACH_1(f,__VA_ARGS__)
#define APPLYFOREACH_3(f,a,...) f(a) APPLYFOREACH_2(f,__VA_ARGS__)
#define APPLYFOREACH_4(f,a,...) f(a) APPLYFOREACH_3(f,__VA_ARGS__)
#define APPLYFOREACH_N(_4,_3,_2,_1,N,...)\
APPLYFOREACH##N
#define APPLYFOREACH(f, ...) \
APPLYFOREACH_N(__VA_ARGS__,_4,_3,_2,_1)(f, ##__VA_ARGS__)
int lib_print_int(int c) {
printf("%d", c);
}
void lib_print_char(char c) {
printf("%c", c);
}
void lib_print_charp(const char *c) {
printf("%s", c);
}
void lib_print_float(float c) {
printf("%f", c);
}
#define lib_print_one(x) \
_Generic((x) \
, int: lib_print_int \
, char: lib_print_char \
, char*: lib_print_charp \
, const char *: lib_print_charp \
, float: lib_print_float \
)(x);
#define lib_print(...) do { \
APPLYFOREACH(lib_print_one, __VA_ARGS__) \
} while(0)
int main() {
lib_print(1, ", ", 1.0f, (char)'\n');
}
注意:_Generic旨在像_Generic(x, int: functionpointers1, double: functionpointer2)(arguments, argument2). 如果你這樣做_Generic(x, char: printf("%c", c),你應該得到很多不匹配格式字串的編譯器警告。使用函式指標更容易。在這種情況下,您也可以擴展為printf(_Generic(x, char: "%c", int: "%d"), x),但我仍然更喜歡函式指標更簡潔。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/528374.html
標籤:C仿制药宏可变参数宏
上一篇:Typescript抽象類擴展自mixin(class,generic)/mixin(classA,classB|classD...)
