Is there a way to create a std::string
(or char*
) array with a pre-processor macro?
Something like this:
std::string myStrings[] = {MAGIC_MACRO(a, b, c)};
Result:
std::string myStrings[] = {"a", "b", "c"}
I know it looks pointless but I need it in a more complicated macro that has a variable number of arguments
Here is how an array of C string can be initialized: #define NUMBER_OF_STRING 4 #define MAX_STRING_SIZE 40 char arr[NUMBER_OF_STRING][MAX_STRING_SIZE] = { "array of c string", "is fun to use", "make sure to properly", "tell the array size" };
A String Array is an Array of a fixed number of String values. A String is a sequence of characters. Generally, a string is an immutable object, which means the value of the string can not be changed. The String Array works similarly to other data types of Array.
In the C Programming Language, the #define directive allows the definition of macros within your source code. These macro definitions allow constant values to be declared for use throughout your code. Macro definitions are not variables and cannot be changed by your program code like variables.
The code below is working for what you've asked for with up to 1024 arguments and without using additional stuff like boost. It defines an EVAL(...)
and also a MAP(m, first, ...)
macro to do recursion and to use for each iteration the macro m
with the next parameter first
.
With the use of that, your MAGIC_MACRO(...)
looks like: #define MAGIC_MACRO(...) EVAL(MAP(STRINGIZE, __VA_ARGS__))
.
It is mostly copied from C Pre-Processor Magic. It is also great explained there. You can also download these helper macros like EVAL(...)
at this git repository, there are also a lot of explanation in the actual code. It is variadic so it takes the number of arguments you want.
But I changed the FIRST
and the SECOND
macro as it uses a Gnu extension like it is in the source I've copied it from.
Main function part:
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" };
}
Macro definitions:
#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__))
Maybe there's a more efficient way, but you can simply use 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
Simple solution is to have a separate macro for each different count. Using 2-level "stringify" macro pattern (read more about it here) you can do something like this:
#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;
}
Output:
strings1: "a"
strings2: "a", "b"
strings3: "a", "b", "c"
If you want just one macro which takes variable number of arguments, it gets a bit messy, as shown in the other answers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With