C ++宏创建一个字符串数组

时间:2017-06-01 09:08:56

标签: c++ preprocessor

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

这样的事情:

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

结果:

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

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

4 个答案:

答案 0 :(得分:4)

以下代码适用于您所要求的最多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__))

答案 1 :(得分:3)

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

Ext.require([
    'Ext.chart.Chart'
]);

Ext.define('CricketScore', {
    extend: 'Ext.data.Model',
    fields: ['month', 'data1' ]
});

var store = Ext.create('Ext.data.Store', {
    model: 'CricketScore',
            data: [
                { month: 'Jan', data1: 20 },
                { month: 'Feb', data1: 20 },
                { month: 'Mar', data1: 19 },
                { month: 'Apr', data1: 18 },
                { month: 'May', data1: 18 },
                { month: 'Jun', data1: 17 },
                { month: 'Jul', data1: 16 },
                { month: 'Aug', data1: 16 },
                { month: 'Sep', data1: 16 },
                { month: 'Oct', data1: 16 },
                { month: 'Nov', data1: 15 },
                { month: 'Dec', data1: 15 }
            ]
});

Ext.create('Ext.chart.Chart', {
   //renderTo: Ext.getBody(),
   renderTo: "demoChart",
   width: 400,
   height: 300,
    store: store,
    animate: true,
   axes: [
       {
                type: 'Numeric',
                position: 'bottom',
                fields: ['data1'],
                minimum: 0,
                maximum:100

            }, {
                type: 'Category',
                position: 'top',
                fields: ['month'],
            }
    ],

      series: [
       {
                type: 'column',
                axis: 'left',
                xField: 'month',
                yField: 'data1',
                style :{
                    fill : '#343234',
                    'stroke-width': 30
                },
                highlight: {
                    fill: '#cd1c5f',
                    'stroke-width': 10,
                    stroke: '#ed2b74'
                },
                tips: {
                    trackMouse: true,
                    style: 'background: #FFF',
                    height: 20,
                    renderer: function(storeItem, item) {
                        this.setTitle(storeItem.get('month') + ': ' + storeItem.get('data1') + '%');
                    }
                }
            }
    ]
});

See it live on Coliru

答案 2 :(得分:2)

简单的解决方案是为每个不同的计数设置一个单独的宏。使用2级“stringify”宏模式(阅读更多关于它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"

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

答案 3 :(得分:0)

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

如,

#define FOREACH_APPLY(GENERATE) \
        GENERATE(a)   \
        GENERATE(b)  \
        GENERATE(c)

#define GENERATE_STRING(STRING) #STRING,

std::string myStrings[] = {
    FOREACH_APPLY(GENERATE_STRING)
};