使用 X-Macros 定义匿名枚举会产生编译错误

define anonymous enum with x-macros produces compilation error

提问人:danwgh 提问时间:11/27/2022 最后编辑:danwgh 更新时间:11/28/2022 访问量:98

问:

我正在做一个大项目,我有很多错误宏。
我想为记录器编写一个辅助函数,将这些 errno 中的每一个字符串化为一个字符串。我决定使用 x-macros,但我收到编译错误

首先,代码是这样的:

// project_errno.h
#define PROJECT_ERR_KEY_FAILURE                         12222
#define PROJECT_ERR_CIPHER_ZERO_PADDING                 12345
#define PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY             14004

我的整理方式如下:

  • 在另一个文件中,我放置了 x-macros:
// project_errno.hx
PROJECT_ERR_FUNC(PROJECT_ERR_KEY_FAILURE)                         12222
PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING)                 12345
PROJECT_ERR_FUNC(PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY)             14004
  • 然后我把它变成了一个枚举:
// project_errno.h
enum {
#define PROJECT_ERR_FUNC(name, value) name=value,
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
};
  • 然后我添加了一个将由记录器使用的函数:
// logging.h (declaration) and (definition) logging.c
const char* stringify_errno(int errno) {
    switch (errno) {
#define PROJECT_ERR_FUNC(name, value) case name: return #value ;
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
    }
}

所以,看起来不错,但我无法让它编译,我得到以下编译错误:

project_errno.h:8:53: error: error: expected identifier before numeric constant
#define PROJECT_ERR_CIPHER_ZERO_PADDING                 12345
                                                        ^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
                                      ^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING,       12345)

project_errno.h:8:53: error: error: expected ‘}’ before numeric constant 
#define PROJECT_ERR_CIPHER_ZERO_PADDING                 12345
                                                        ^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
                                      ^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING,       12345)

project_errno.h:8:53: error: expected unqualified-id before numeric constant 
#define PROJECT_ERR_CIPHER_ZERO_PADDING                 12345
                                                        ^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
                                      ^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING,       12345)
                  ^~~~~~~~~~~~~~~~~~~~~~~
In file included from ......../project_errno.h:20:1: error: expected declaration before ‘}’ token
};
^

..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
                                      ^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING,       12345)
                  ^~~~~~~~~~~~~~~~~~~~~~~
    

我不明白为什么我会收到这些错误(我在同一编译会话中多次收到相同的错误消息),我希望你们能帮助我。 另外,如果您有任何其他解决方案来解决我最初打算解决的问题(使用 errno 宏并添加一个函数来字符串化这些 errno,每当我向项目添加 errno [仅在一个地方]),我很想听听谢谢

C++ C X 宏

评论

0赞 fabian 11/27/2022
只需将标头中的代码复制并粘贴到包含它的文件中,您应该会看到 a) 您传递了错误数量的参数,并且 b) 假设编译器会认为第二个参数为空,您将得到 .另外,在最后一个常量的末尾,这些反引号在做什么?12222 = , 12222 12345 ...
0赞 danwgh 11/27/2022
我不确定你的意思。你怎么称呼标头,你怎么称呼文件(有 project_errno.h 和 project_errno.hx,你称呼它们中的哪一个) 关于你的亮点:a) 我什么时候传递错误数量的参数?我总是传递 2 个参数(名称、值) b) 为什么编译器会认为第二个参数为空?我给每个 x-macro 两个参数。似乎你看到了一些我没有看到的东西,我很想知道这个:)谢谢

答:

0赞 Rulle 11/27/2022 #1

编辑答案:

我能够使用

enum {
#define PROJECT_ERR_FUNC(name) , name =
PROJECT_ERR_START_OF_ENUM = -1
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
};

但必须删除末尾的反引号。上面的代码只有在你之前没有包含的情况下才有效。project_errno.hxproject_errno.h

一个完整的最低工作计划将是

enum {
#define PROJECT_ERR_FUNC(name) , name =
PROJECT_ERR_START_OF_ENUM = -1
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
};

#include <iostream>

int main() {
  std::cout << PROJECT_ERR_KEY_FAILURE << std::endl;
  return 0;
}

这将打印.12222

评论

0赞 danwgh 11/27/2022
首先,它:(没有帮助。我收到相同的错误消息,但有一个额外的换行,在该错误消息的所有混乱中 其次,您需要您建议的解决方案,我们在同一文件中使用它。但在这里,我使用 2 个单独的文件,所以我不需要定义那个 FOREACH,而只需要将“纯文本”放在 project_errno.hx 中并包含它
0赞 Rulle 11/27/2022
@danwgh 为了让它工作,你必须从小处着手:首先复制我的最低工作程序并让它工作。然后从那里继续前进......
0赞 danwgh 11/27/2022
我不明白我怎么能让它工作。正如您所定义的,宏函数甚至不会产生“值”。请注意,我使用的是非连续的数字,所以我不能相信枚举会自己生成这些值。
0赞 Bob__ 11/28/2022 #2

我会按照维基百科页面中关于 X 宏的食谱进行操作:

实现

X 宏应用程序由两部分组成:

  1. 列表元素的定义。
  2. 扩展列表以生成声明或语句的片段。

该列表由宏或头文件(命名)定义,该文件本身不生成任何代码,而仅由宏(经典命名)的一系列调用和元素的数据组成。每个扩展 的前面都有一个定义 with with 列表元素的语法。对列表中每个元素的 expands 的调用。LISTXLISTXLISTX

特别是第二个示例,即以 X 宏为参数的示例

将工作器宏的名称传递到列表宏中。这既避免了定义名称晦涩的宏 (),又减轻了取消定义它的需要。X

在 OP 的用例中,这会导致以下三个文件:

// project_errno.h
#ifndef PROJECT_ERRNO_H
#define PROJECT_ERRNO_H

#define FOR_EACH_ERR_ID_VALUE_PAIR(DO) \
    DO(PROJECT_ERR_KEY_FAILURE,             12222) \
    DO(PROJECT_ERR_CIPHER_ZERO_PADDING,     12345) \
    DO(PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY, 14004)

#define DEFINE_ENUM_ITEM(err, value) err = value,
enum project_errs {
    FOR_EACH_ERR_ID_VALUE_PAIR( DEFINE_ENUM_ITEM )
};
#undef DEFINE_ENUM_ITEM

#endif
// logging.h
#ifndef LOGGING_H
#define LOGGING_H

const char* stringify_errno(int errno);

#endif
// logging.c
#include <stdio.h>
#include "project_errno.h"

#define STRINGIFY_ERR_VALUE_NAME(name, value) case name: \
    return "[" #value "] " #name;
 
const char* stringify_errno(int errno)
{
    switch (errno) {
        FOR_EACH_ERR_ID_VALUE_PAIR(STRINGIFY_ERR_VALUE_NAME)
        default:
            return "[-----] UNKWOWN";    
    }
}

#undef STRINGIFY_ERR_VALUE_NAME

可在此处测试:https://wandbox.org/permlink/aNJCI7lQihkFnYzp