通过预处理器宏生成常量
Generating constant via preprocessor macros
我想创建一个宏来吐出一个存在的常量。
有多个常量,它们都遵循PREFIX_COMPONENT_ERROR
.
的形式
示例代码:
#include <stdlib.h>
enum {
MODULE_ERROR_COMP1_ERROR1 = 0,
MODULE_ERROR_COMP1_ERROR2,
MODULE_ERROR_COMP1_ERROR3,
MODULE_ERROR_COMP2_ERROR1,
MODULE_ERROR_COMP2_ERROR2,
MODULE_ERROR_COMP2_ERROR3,
};
static void* some_function (const char *restrict input);
#define EMPTY(...)
#define DEFER(...) __VA_ARGS__ EMPTY()
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
#define EXPAND(...) __VA_ARGS__
#define PASTER(x, y) x ## _ ## y
#define MODULE_ERROR some_function ("module error")
#define ERROR_PREFIX DEFER(MODULE_ERROR)
#define GENERATE_ERROR_(prefix, component, error) PASTER(DEFER(PASTER(prefix, component)), error)
#define GENERATE_ERROR(prefix, component, error) EXPAND(GENERATE_ERROR_(prefix, component, error))
static void* some_function (const char *restrict input) {
/*
* Does something with the input and returns some data,
* but for simplicity let's assume it just passes the input through.
*/
return (input);
}
int main (int argc, char **argv) {
/* Generate enum via PP macros, for instance: */
for (size_t i = 0; 3 > i; ++i) {
int error_code = 0;
void *common_error = ERROR_PREFIX;
if (0 == i) {
error_code = GENERATE_ERROR (ERROR_PREFIX, COMP1, ERROR1);
}
else if (1 == i) {
error_code = GENERATE_ERROR (ERROR_PREFIX, COMP2, ERROR3);
}
else {
error_code = GENERATE_ERROR (ERROR_PREFIX, COMP2, ERROR2);
}
/* Do something with error_code and common_error. */
}
return (EXIT_SUCCESS);
}
我想知道我想做的事情是否可以通过简单的 PP 指令实现。
第一个问题是我不能两次使用连接宏PASTER
,因为PP总是在内部PASTER()
调用时阻塞。
将其转换为三参数宏,例如:
#define PASTER(x, y, z) x ## _ ## y ## _ ## z
#define GENERATE_ERROR_(prefix, component, error) PASTER(prefix, component, error)
将扩展 prefix
,这是我不想发生的,并且无论如何 运行 都会出错。
使用类似 PASTER(OBSTRUCT(prefix), component, error)
的方式同样会在连接时失败。
我完全知道这会起作用,但我不喜欢它:
#define MODULE_ERROR_FUNC some_function ("module error")
#define ERROR_PREFIX MODULE_ERROR
#define GENERATE_ERROR_(prefix, component, error) PASTER(prefix, component, error)
#define GENERATE_ERROR(prefix, component, error) GENERATE_ERROR_(prefix, component, error)
/* [...] */
void *common_error = MODULE_ERROR_FUNC;
真的没有办法让 ERROR_PREFIX
在 GENERATE_ERROR
宏中只展开一次(即 MODULE_ERROR
),否则让它展开到函数调用(即, some_function ("module error")
)?
看到你的问题我的第一反应是"you're over-complicating it!"例如,有什么问题:
#define GENERATE_ERROR(prefix, component, error) prefix ## _ ## component ## _ ## error
然后调用:
GENERATE_ERROR(MODULE_PREFIX, COMP1, ERROR1)
"what's the problem?" 的部分答案是 "it's not obvious in this example, but when the prefix is something like PROJECT_LONG_SUBMODULE_ERROR
you'd like to avoid typing it all the time"。没关系,但你可以考虑:
#define GENERR(component, error) GENERATE_ERROR(PROJECT_LONG_SUBMODULE_ERROR, component, error)
缩短长名称 — 调用
GENERR(COMPONENT2, ERROR2)
获取错误名称:
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2
Why won't PROJECT_LONG_SUBMODULE_ERROR
be expanded during the expansion of GENERR
(i.e., why is it painted blue in that context)?
它没有漆成蓝色;我只是不会用那个名字创建一个宏,所以它没有任何东西可以扩展到——就像不应该有一个宏 COMPONENT2
或 ERROR2
可以在 [= 时扩展一样19=] 被调用。
实验
考虑源文件pp31.c
:
#define GENERATE_ERROR(prefix, component, error) prefix ## _ ## component ## _ ## error
GENERATE_ERROR(MODULE_PREFIX, COMP1, ERROR1) -- OK
#define GENERR(component, error) GENERATE_ERROR(PROJECT_LONG_SUBMODULE_ERROR, component, error)
GENERR(COMPONENT2, ERROR2) -- OK
#define PLS_ERR PROJECT_LONG_SUBMODULE_ERROR
#undef GENERR
#define GENERR(component, error) GENERATE_ERROR(PLS_ERR, component, error)
GENERR(COMPONENT2, ERROR2) -- Broken
#undef GENERR
#define GENERR_PREFIX(prefix, component, error) GENERATE_ERROR(prefix, component, error)
#define GENERR(component, error) GENERR_PREFIX(PLS_ERR, component, error)
GENERR(COMPONENT2, ERROR2) -- OK
#define PROJECT_LONG_SUBMODULE_ERROR pink_elephant
GENERR(COMPONENT2, ERROR2) -- Broken (too many pink elephants)
#undef PROJECT_LONG_SUBMODULE_ERROR
#define GENCOMPERR(err) GENERATE_ERROR(PLS_ERR, COMPONENT2, err)
GENCOMPERR(ERROR2) -- Broken
#undef GENCOMPERR
#define GENCOMPERR(err) GENERR(COMPONENT2, err)
GENCOMPERR(ERROR2) -- OK
#undef GENCOMPERR
#define CURR_COMP COMPONENT2
#define GENCOMPERR(err) GENERATE_ERROR(PLS_ERR, CURR_COMP, err)
GENCOMPERR(ERROR2) -- Broken
#undef GENCOMPERR
#define GENCOMPERR(err) GENERR_PREFIX(PLS_ERR, CURR_COMP, err)
GENCOMPERR(ERROR2) -- OK
当我 运行 cpp pp31.c
(这是来自 GCC 9.3.0 运行ning 的 cpp
Mac)时,我得到一些空行比下面显示的多,但非空行无论如何都是有趣的:
# 1 "pp31.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "pp31.c"
MODULE_PREFIX_COMP1_ERROR1 -- OK
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
PLS_ERR_COMPONENT2_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
pink_elephant_COMPONENT2_ERROR2 -- Broken (too many pink elephants)
PLS_ERR_COMPONENT2_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
PLS_ERR_CURR_COMP_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
关键点是(小心),您可以使用扩展到最终串联错误消息名称元素的宏(因此 PLS_ERR
和 CURR_COMP
),但您不能这些元素的宏(无 'pink elephants')。
我对这是否是处理错误消息名称的真正好的设计持保留意见,但我认为它可以工作。
我想创建一个宏来吐出一个存在的常量。
有多个常量,它们都遵循PREFIX_COMPONENT_ERROR
.
示例代码:
#include <stdlib.h>
enum {
MODULE_ERROR_COMP1_ERROR1 = 0,
MODULE_ERROR_COMP1_ERROR2,
MODULE_ERROR_COMP1_ERROR3,
MODULE_ERROR_COMP2_ERROR1,
MODULE_ERROR_COMP2_ERROR2,
MODULE_ERROR_COMP2_ERROR3,
};
static void* some_function (const char *restrict input);
#define EMPTY(...)
#define DEFER(...) __VA_ARGS__ EMPTY()
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
#define EXPAND(...) __VA_ARGS__
#define PASTER(x, y) x ## _ ## y
#define MODULE_ERROR some_function ("module error")
#define ERROR_PREFIX DEFER(MODULE_ERROR)
#define GENERATE_ERROR_(prefix, component, error) PASTER(DEFER(PASTER(prefix, component)), error)
#define GENERATE_ERROR(prefix, component, error) EXPAND(GENERATE_ERROR_(prefix, component, error))
static void* some_function (const char *restrict input) {
/*
* Does something with the input and returns some data,
* but for simplicity let's assume it just passes the input through.
*/
return (input);
}
int main (int argc, char **argv) {
/* Generate enum via PP macros, for instance: */
for (size_t i = 0; 3 > i; ++i) {
int error_code = 0;
void *common_error = ERROR_PREFIX;
if (0 == i) {
error_code = GENERATE_ERROR (ERROR_PREFIX, COMP1, ERROR1);
}
else if (1 == i) {
error_code = GENERATE_ERROR (ERROR_PREFIX, COMP2, ERROR3);
}
else {
error_code = GENERATE_ERROR (ERROR_PREFIX, COMP2, ERROR2);
}
/* Do something with error_code and common_error. */
}
return (EXIT_SUCCESS);
}
我想知道我想做的事情是否可以通过简单的 PP 指令实现。
第一个问题是我不能两次使用连接宏PASTER
,因为PP总是在内部PASTER()
调用时阻塞。
将其转换为三参数宏,例如:
#define PASTER(x, y, z) x ## _ ## y ## _ ## z
#define GENERATE_ERROR_(prefix, component, error) PASTER(prefix, component, error)
将扩展 prefix
,这是我不想发生的,并且无论如何 运行 都会出错。
使用类似 PASTER(OBSTRUCT(prefix), component, error)
的方式同样会在连接时失败。
我完全知道这会起作用,但我不喜欢它:
#define MODULE_ERROR_FUNC some_function ("module error")
#define ERROR_PREFIX MODULE_ERROR
#define GENERATE_ERROR_(prefix, component, error) PASTER(prefix, component, error)
#define GENERATE_ERROR(prefix, component, error) GENERATE_ERROR_(prefix, component, error)
/* [...] */
void *common_error = MODULE_ERROR_FUNC;
真的没有办法让 ERROR_PREFIX
在 GENERATE_ERROR
宏中只展开一次(即 MODULE_ERROR
),否则让它展开到函数调用(即, some_function ("module error")
)?
看到你的问题我的第一反应是"you're over-complicating it!"例如,有什么问题:
#define GENERATE_ERROR(prefix, component, error) prefix ## _ ## component ## _ ## error
然后调用:
GENERATE_ERROR(MODULE_PREFIX, COMP1, ERROR1)
"what's the problem?" 的部分答案是 "it's not obvious in this example, but when the prefix is something like PROJECT_LONG_SUBMODULE_ERROR
you'd like to avoid typing it all the time"。没关系,但你可以考虑:
#define GENERR(component, error) GENERATE_ERROR(PROJECT_LONG_SUBMODULE_ERROR, component, error)
缩短长名称 — 调用
GENERR(COMPONENT2, ERROR2)
获取错误名称:
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2
Why won't
PROJECT_LONG_SUBMODULE_ERROR
be expanded during the expansion ofGENERR
(i.e., why is it painted blue in that context)?
它没有漆成蓝色;我只是不会用那个名字创建一个宏,所以它没有任何东西可以扩展到——就像不应该有一个宏 COMPONENT2
或 ERROR2
可以在 [= 时扩展一样19=] 被调用。
实验
考虑源文件pp31.c
:
#define GENERATE_ERROR(prefix, component, error) prefix ## _ ## component ## _ ## error
GENERATE_ERROR(MODULE_PREFIX, COMP1, ERROR1) -- OK
#define GENERR(component, error) GENERATE_ERROR(PROJECT_LONG_SUBMODULE_ERROR, component, error)
GENERR(COMPONENT2, ERROR2) -- OK
#define PLS_ERR PROJECT_LONG_SUBMODULE_ERROR
#undef GENERR
#define GENERR(component, error) GENERATE_ERROR(PLS_ERR, component, error)
GENERR(COMPONENT2, ERROR2) -- Broken
#undef GENERR
#define GENERR_PREFIX(prefix, component, error) GENERATE_ERROR(prefix, component, error)
#define GENERR(component, error) GENERR_PREFIX(PLS_ERR, component, error)
GENERR(COMPONENT2, ERROR2) -- OK
#define PROJECT_LONG_SUBMODULE_ERROR pink_elephant
GENERR(COMPONENT2, ERROR2) -- Broken (too many pink elephants)
#undef PROJECT_LONG_SUBMODULE_ERROR
#define GENCOMPERR(err) GENERATE_ERROR(PLS_ERR, COMPONENT2, err)
GENCOMPERR(ERROR2) -- Broken
#undef GENCOMPERR
#define GENCOMPERR(err) GENERR(COMPONENT2, err)
GENCOMPERR(ERROR2) -- OK
#undef GENCOMPERR
#define CURR_COMP COMPONENT2
#define GENCOMPERR(err) GENERATE_ERROR(PLS_ERR, CURR_COMP, err)
GENCOMPERR(ERROR2) -- Broken
#undef GENCOMPERR
#define GENCOMPERR(err) GENERR_PREFIX(PLS_ERR, CURR_COMP, err)
GENCOMPERR(ERROR2) -- OK
当我 运行 cpp pp31.c
(这是来自 GCC 9.3.0 运行ning 的 cpp
Mac)时,我得到一些空行比下面显示的多,但非空行无论如何都是有趣的:
# 1 "pp31.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "pp31.c"
MODULE_PREFIX_COMP1_ERROR1 -- OK
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
PLS_ERR_COMPONENT2_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
pink_elephant_COMPONENT2_ERROR2 -- Broken (too many pink elephants)
PLS_ERR_COMPONENT2_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
PLS_ERR_CURR_COMP_ERROR2 -- Broken
PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2 -- OK
关键点是(小心),您可以使用扩展到最终串联错误消息名称元素的宏(因此 PLS_ERR
和 CURR_COMP
),但您不能这些元素的宏(无 'pink elephants')。
我对这是否是处理错误消息名称的真正好的设计持保留意见,但我认为它可以工作。