【问题标题】:c++ macro recognizing tokens as argumentsc ++宏将标记识别为参数
【发布时间】:2016-01-31 02:02:28
【问题描述】:

所以,我已经有一段时间没有用 C++ 编写任何东西了,现在我正在使用 C++11 和宏来开发一个项目。

我知道通过使用 stringify 运算符我可以做到这一点:

#define TEXT(a) #a    //expands to "a"

我应该如何使用预处理器来识别 + 和 * 之类的标记来做到这一点:

#define TEXT(a)+ ???  //want to expand to "a+"
#define TEXT(a)* ???  //want to expand to "a*"

什么时候输入必须是那个语法?

我试过这样做:

#define + "+"

但它当然不起作用。如何让预处理器识别这些标记?

注意: 这实际上是定义和使用正则表达式的小型语言项目的一部分,其中宏的结果字符串将在 regex 中使用。语法是给定的,我们必须照原样使用它而不对其进行任何更改。

例如 TEXT(a)+ 用于制作正则表达式:std::regex("a+") 不改变TEXT(a) 扩展为“a”的事实

【问题讨论】:

    标签: c++ c++11 macros c-preprocessor


    【解决方案1】:

    首先,

    #define TEXT(a) #a
    

    不会“转换为"a"”。 a 只是一个参数的名称。宏扩展为一个字符串,其中包含调用 TEXT 的任何内容。所以TEXT(42 + rand()) 将扩展为"42 + rand()"。请注意,如果您将宏作为参数传递,宏将不会被扩展。 TEXT(EXIT_SUCCESS) 将扩展为 "EXIT_SUCCESS",而不是 "0"。如果您想要完全扩展,请添加一个额外的间接层并将参数传递给 TEXT 到另一个执行字符串化的宏 TEXT_R

    #define TEXT_R(STUFF)   # STUFF
    #define TEXT(STUFF)     TEXT_R(STUFF)
    

    其次,我不太清楚你所说的 TEXT(a)+TEXT(a)* 是什么意思。例如,您想将TEXT(foo) 扩展到"foo+" 吗?我认为在这种情况下最简单的解决方案是使用隐式字符串文字连接。

    #define TEXT_PLUS(STUFF)  # STUFF "+"
    #define TEXT_STAR(STUFF)  # STUFF "*"
    

    或者,如果您想要完全扩展。

    #define TEXT_R(STUFF)     # STUFF
    #define TEXT_PLUS(STUFF)  TEXT_R(STUFF+)
    #define TEXT_STAR(STUFF)  TEXT_R(STUFF*)
    

    【讨论】:

    • 首先,感谢您让我更清楚。我已经纠正了我的“转换”错误。其次,是的,这正是我想要做的,但我无法更改 TEXT(a)+ 的语法。它必须在代码中以完全相同的方式编写并扩展为“a+”
    • 我不相信这是可能的。 可能的,困难的是使用一袋模板元编程和使用运算符重载。缺点是你必须写+TEXT(a)*TEXT(a)。但我认为这是你能得到的最接近的。
    【解决方案2】:

    你的作业不可能用 C++ 解决。您要么误解了某些内容,要么项目规范中有错误。无论如何,我们在这里遇到了问题:

    TEXT(a)+ 用于制作正则表达式:std::regex("a+") 不改变TEXT(a) 扩展为"a"的事实 [我的重点]

    TEXT(a) 扩展为 "a" — 意思是,我们可以在示例中的任何位置替换 TEXT(a);毕竟,这正是预处理器所做的。换句话说,您希望编译器转换此 C++ 代码

    "a"+
    

    进入

    std::regex("a+")
    

    这根本不可能,因为 C++ 预处理不允许扩展 + 令牌。

    在 C++ 中我们能做的最好的事情就是使用运算符重载来生成所需的代码。但是,有两个障碍:

    • 您只能在自定义类型上重载运算符,而"a" 不是自定义类型;它的类型是char const[2](为什么是2?空终止!)。

    • Postfix-+ 不是有效的 C++ 运算符,不能重载。

    如果你的任务只是有点不同,它会起作用的。事实上,如果你的任务说 TEXT(a)++ 应该产生预期的结果,并且你可以更改 TEXT 的定义以输出 "a" 以外的其他内容,那么我们就可以做生意了:

    #include <string>
    #include <regex>
    
    #define TEXT(a) my_regex_token(#a)
    
    struct my_regex_token {
        std::string value;
    
        my_regex_token(std::string value) : value{value} {}
    
        // Implicit conversion to `std::regex` — to be handled with care.
    
        operator std::regex() const {
            return std::regex{value};
        }
    
        // Operators
    
        my_regex_token operator ++(int) const {
            return my_regex_token{value + "+"};
        }
    
        // more operators …
    };
    
    int main() {
        std::regex x = TEXT(a)++;
    }
    

    【讨论】:

      【解决方案3】:

      您不想将字符插入宏的末尾。

      也许你只是想要这样的东西:

      #define TEXT(a, b) #a #b
      

      这样TEXT(a, +) 会扩展为“a”“+”,TEXT(a, *) 会扩展为“a”“*”

      如果您需要确切的语法,请使用辅助宏,例如:

      #define TEXT(a) #a
      #define ADDTEXT(x, y) TEXT(x ## y)
      

      这样,ADDTEXT(a, +) 扩展为“a+”,ADDTEXT(a, *) 扩展为“a*”

      【讨论】:

      • 嗯,是的,我也考虑过这个解决方案,但输入必须采用精确的语法......
      • @NenaB 您是说“必须采用准确的语法”,但您发布的内容根本不是合法的 C++。你想如何使用宏?
      • ## 运算符必须用于组合两个标识符或部分标识符;您不能使用它来粘贴标识符和运算符符号。例如,当y*+ 时,ADDTEXT(x, y) 将无法正常工作。您的编译器是否真的抱怨可能取决于您设置的警告级别 - 但正式地说,它不起作用。 如果 [连接两个标记] 的结果不是有效的预处理标记,则行为未定义。 这意味着它可能有效,但同样可能无效。
      • @KonradRudolph 阅读添加的说明
      【解决方案4】:

      你也可以这样做:

      #define TEXT(a) "+"  // "a" "+" -> "a+"
      #define TEXT(a) "*"  // "a" "*" -> "a*"
      

      C/C++ 中的两个字符串文字将按规范连接成单个文字。

      【讨论】:

      • 如果没有#undef 的介入,您不能使用不同的替换顺序重新定义宏,并且您不能让两个同名的宏同时执行不同的工作。这个问题也不清楚,看起来 - 所以这是问题的结转。
      猜你喜欢
      • 1970-01-01
      • 2012-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-14
      • 2015-09-07
      相关资源
      最近更新 更多