【问题标题】:C++ Macro to create a string array用于创建字符串数组的 C++ 宏
【发布时间】:2017-06-01 09:08:56
【问题描述】:

有没有办法使用预处理器宏创建std::string(或char*)数组?

类似这样的:

std::string myStrings[] = {MAGIC_MACRO(a, b, c)};

结果:

std::string myStrings[] = {"a", "b", "c"}

我知道它看起来毫无意义,但我需要在一个具有可变数量参数的更复杂的宏中使用它

【问题讨论】:

  • 不,你不能这样做。特别不是用[]替换{}的方法。
  • 结果部分不清楚抱歉,看我的编辑
  • 参数的数量是否有限制,还是应该支持任意数量?
  • 没有限制。类似MAGIC_MACRO(...)
  • 这可能取决于“更复杂的宏”当前的样子。

标签: c++ preprocessor


【解决方案1】:

下面的代码可以使用多达 1024 个参数来满足您的要求,并且没有使用诸如 boost 之类的其他东西。它定义了一个EVAL(...) 和一个MAP(m, first, ...) 宏来进行递归,并为每次迭代使用宏m 和下一个参数first

使用它,您的MAGIC_MACRO(...) 看起来像:#define MAGIC_MACRO(...) EVAL(MAP(STRINGIZE, __VA_ARGS__))

大部分抄自C Pre-Processor Magic。那里也有很好的解释。你也可以在这个git repository下载EVAL(...)这样的帮助宏,实际代码中也有很多解释。它是可变参数,所以它需要你想要的参数数量。

但我更改了 FIRSTSECOND 宏,因为它使用 Gnu 扩展,就像它在我复制的源中一样。

主要功能部分:

int main()
{
   std::string myStrings[] = { MAGIC_MACRO(a, b, c) }; // Expands to: std::string myStrings[] = { "a" , "b" , "c" };
   std::string myStrings[] = { MAGIC_MACRO(a, b, c, x, y, z) }; // Expands to: std::string myStrings[] = { "a" , "b" , "c", "x" , "y" , "z" };
}

宏定义:

#define FIRST_(a, ...) a
#define SECOND_(a, b, ...) b

#define FIRST(...) FIRST_(__VA_ARGS__,)
#define SECOND(...) SECOND_(__VA_ARGS__,)

#define EMPTY()

#define EVAL(...) EVAL1024(__VA_ARGS__)
#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) __VA_ARGS__

#define DEFER1(m) m EMPTY()
#define DEFER2(m) m EMPTY EMPTY()()

#define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
#define PROBE() ~, 1

#define CAT(a,b) a ## b

#define NOT(x) IS_PROBE(CAT(_NOT_, x))
#define _NOT_0 PROBE()

#define BOOL(x) NOT(NOT(x))

#define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
#define _IF_ELSE(condition) CAT(_IF_, condition)

#define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
#define _IF_0(...)             _IF_0_ELSE

#define _IF_1_ELSE(...)
#define _IF_0_ELSE(...) __VA_ARGS__

#define COMMA ,

#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
#define _END_OF_ARGUMENTS_() 0

#define MAP(m, first, ...)           \
  m(first)                           \
  IF_ELSE(HAS_ARGS(__VA_ARGS__))(    \
    COMMA DEFER2(_MAP)()(m, __VA_ARGS__)   \
  )(                                 \
    /* Do nothing, just terminate */ \
  )
#define _MAP() MAP

#define STRINGIZE(x) #x
#define MAGIC_MACRO(...) EVAL(MAP(STRINGIZE, __VA_ARGS__))

【讨论】:

    【解决方案2】:

    也许有更有效的方法,但您可以简单地使用 Boost.PP:

    #define MAGIC_MACRO_ELEM(r, data, i, elem) \
        BOOST_PP_COMMA_IF(i) BOOST_PP_STRINGIZE(elem)
    
    #define MAGIC_MACRO(...) \
        BOOST_PP_SEQ_FOR_EACH_I(MAGIC_MACRO_ELEM, ~, BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__)))
    

    See it live on Coliru

    【讨论】:

    • 谢谢,但我不打算使用 boost
    • @oneiros:您可以使用the preprocessor library,而无需拖入 Boost 的其余部分。 (“Boost Preprocessing 库是一个宏库,支持预处理器元编程。该库支持 C++ 和 C 编译。它不依赖于任何其他 Boost 库,因此可以用作独立库。”)所以你只需下载它并将其放入您的包含目录即可。没有二进制文件;该库仅包含宏定义。
    • @Oneiros:为什么不呢?
    • 我在寻找一个标准的无依赖解决方案
    • @Oneiros:您自己的代码完全是一个依赖项。
    【解决方案3】:

    简单的解决方案是为每个不同的计数设置一个单独的宏。使用 2 级“字符串化”宏模式(阅读更多关于它的信息 here),您可以执行以下操作:

    #include <iostream>
    #include <sstream>
    
    #define XSTRINGIFY(s) #s
    
    #define STRINGARRAY1(s0) { XSTRINGIFY(s0) }
    #define STRINGARRAY2(s0, s1) { XSTRINGIFY(s0), XSTRINGIFY(s1) }
    #define STRINGARRAY3(s0, s1, s2) { XSTRINGIFY(s0), XSTRINGIFY(s1), XSTRINGIFY(s2) }
    
    using namespace std;
    
    string dumpStrings(string *array, int count) {
        stringstream ss;
        if (count > 0) {
            ss << '"' << array[0] << '"';
            for(int i = 1; i < count; ++i) {
                ss << ", \"" << array[i]<< '"';
            }
        }
        return ss.str();
    }
    
    int main()
    {
        string strings1[1] = STRINGARRAY1(a);
        string strings2[2] = STRINGARRAY2(a, b);
        string strings3[3] = STRINGARRAY3(a, b, c);
        cout << "strings1: " << dumpStrings(strings1, sizeof(strings1) / sizeof(strings1[0])) << endl;
        cout << "strings2: " << dumpStrings(strings2, sizeof(strings2) / sizeof(strings2[0])) << endl;
        cout << "strings3: " << dumpStrings(strings3, sizeof(strings3) / sizeof(strings3[0])) << endl;
    }
    

    输出:

    strings1: "a"
    strings2: "a", "b"
    strings3: "a", "b", "c"
    

    如果您只想要一个带有可变数量参数的宏,它会变得有点混乱,如其他答案所示。

    【讨论】:

    • 除非万不得已,否则请不要将多条语句放在一行中。它严重影响可读性。至于现代化建议:&lt;array&gt;
    • 其实如果dumpStrings返回一个字符串,就会变成1条语句就行了
    • 是的。稍作修改。
    【解决方案4】:

    我不确定这是否符合您想要实现的目标,但我通常使用this technique 生成字符串(或从字符串生成的其他列表,如枚举元素)。

    例如,

    #define FOREACH_APPLY(GENERATE) \
            GENERATE(a)   \
            GENERATE(b)  \
            GENERATE(c)
    
    #define GENERATE_STRING(STRING) #STRING,
    
    std::string myStrings[] = {
        FOREACH_APPLY(GENERATE_STRING)
    };
    

    【讨论】:

    • 这也是我习惯的。有没有办法用模板之类的东西代替它?
    猜你喜欢
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-15
    • 2017-03-29
    • 2019-03-20
    • 1970-01-01
    相关资源
    最近更新 更多