首页 > 解决方案 > 通过预处理器宏生成常量

问题描述

我想创建一个吐出现有常量的宏。

有多个常量,它们都遵循形式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)

will expand 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(即 to MODULE_ERROR),否则让它扩展到函数调用(即some_function ("module error"))?

标签: cc-preprocessor

解决方案


看到你的问题,我的第一反应是“你太复杂了!” 例如,有什么问题:

#define GENERATE_ERROR(prefix, component, error) prefix ## _ ## component ## _ ## error

然后调用:

GENERATE_ERROR(MODULE_PREFIX, COMP1, ERROR1)

“有什么问题?”的部分答案。是“在此示例中并不明显,但是当前缀类似于PROJECT_LONG_SUBMODULE_ERROR您希望避免一直键入它时”。这没关系,但你可以考虑:

#define GENERR(component, error) GENERATE_ERROR(PROJECT_LONG_SUBMODULE_ERROR, component, error)

缩短长名称——调用

GENERR(COMPONENT2, ERROR2)

获取错误名称:

PROJECT_LONG_SUBMODULE_ERROR_COMPONENT2_ERROR2

为什么在扩展过程中不会PROJECT_LONG_SUBMODULE_ERROR被扩展GENERR(即,为什么在那种情况下它被涂成蓝色)?

它不是蓝色的;我只是不会创建一个具有该名称的宏,因此它没有什么可以扩展的——就像不应该有宏COMPONENT2或者在调用ERROR2时可以扩展一样。GENERR


实验

考虑源文件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(这是在cppMac 上运行的 GCC 9.3.0),我得到的空行比下面显示的要多,但非空行仍然是有趣的:

# 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_ERRCURR_COMP),但您不能拥有这些元素的宏(没有“粉红大象”)。


我保留判断这是否真的是处理错误消息名称的好设计,但我认为它可以工作。


推荐阅读