Preprocessor initialize array - c++

How, if necessary, using boost preprocessor, initialize the array as follows:
INIT_ARRAY(plus,minus)
//extract to
std::array<std::pair<char const *,std::string>, 2> array{{
{"plus", std::string("plus")}, {"minus", std::string("minus")} }};
INIT_ARRAY(plus,minus,multiply)
//extract to
std::array<std::pair<char const *,std::string>, 3> array{{
{"plus", std::string("plus")}, {"minus", std::string("minus")}, {"multiply", std::string("multiply")} }};
P.S. Couldn't solve the problem with the last comma in the initializer (...{n-1} , {n} ,)
and counting the number of arguments to pass to std::array<...,n>. I use the preprocessor, since the names passed to the macro will be used later for code generation. Used c++20

You can use BOOST_PP_COMMA_IF for conditional commas, and BOOST_PP_VARIADIC_SIZE to work out the size beforehand.
This should work (for non-empty arrays):
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/variadic/size.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#define INIT_ARRAY_INITIALIZER(r, _, i, e) BOOST_PP_COMMA_IF(i) {BOOST_PP_STRINGIZE(e), std::string(BOOST_PP_STRINGIZE(e))}
#define INIT_ARRAY(...) \
std::array<std::pair<char const *, std::string>, \
BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)> array{{ \
BOOST_PP_SEQ_FOR_EACH_I(INIT_ARRAY_INITIALIZER, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
}};

Related

BOOST_PP expand sequence in the empty sequence case

Using BOOST_PP I can expand a macro into multiple comma separated values with an additional token, as can be seen in the below code.
However, it doesn't work in the no-argument case.
#define BOOST_PP_VARIADICS
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#define ADD_TOKEN(r, token, i, e) \
BOOST_PP_COMMA_IF(i) token(e)
#define WRAP(...) \
BOOST_PP_SEQ_FOR_EACH_I(ADD_TOKEN, decltype, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
#define MACRO(fmt, ...) \
Template<WRAP(__VA_ARGS__)>
MACRO("");
MACRO("", 0);
MACRO("", 0, 1);
The output when compiling with gcc -E main.cpp is
Template< decltype() >;
Template< decltype(0) >;
Template< decltype(0) , decltype(1) >;
How can I get calls to MACRO with no __VA_ARGS__ arguments expand to null?
That is, I'd like the output to be:
Template< >;
Template< decltype(0) >;
Template< decltype(0) , decltype(1) >;
How can I achieve this?
This answer uses a GNU extension. You stated in the comments that you're okay with that.
You can use BOOST_PP_TUPLE_SIZE((, ## __VA_ARGS__)): it will give you 1 if and only if the variadic arguments are omitted.
Templates are a bit tricky in that they can contain unparenthesised commas, which cause confusion when used in macro arguments. It takes a bit of work to write it in such a way that the WRAP macro is only expanded after BOOST_PP_IF has finished already:
#define MACRO(fmt, ...) \
Template< \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE((,##__VA_ARGS__)), 1), \
BOOST_PP_EXPAND, WRAP) (__VA_ARGS__) \
>
Note: I'm using BOOST_PP_EXPAND in the empty case, because BOOST_PP_EXPAND(__VA_ARGS__) will expand to nothing.

How do I expand a macro containing commas inside a BOOST_PP_IF

I asked the following question earlier, but the solution doesn't seem to work in this particular case.
How do I print out a comma multiple times using Boost Preprocessor
I am trying to expand a macro containing a comma conditionally. Here is an example illustrating the problem:
#define TEST(...)\
BOOST_PP_REPEAT( \
BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), \
MACRO, \
BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__))
#define MACRO(z, n, data) BOOST_PP_IF(1,MACRO_CONTAINING_COMMA(z, z),MACRO_CONTAINING_COMMA(z, z))
#define MACRO_CONTAINING_COMMA(_NAME, _NAME2) _NAME TIBRA_EATEN_COMMA() _NAME2
#define EATEN_COMMA BOOST_PP_IF(1,BOOST_PP_COMMA,BOOST_PP_TUPLE_EAT())
TEST(1,2,3,4)
This expands to
BOOST_PP_IIF BOOST_PP_IIF BOOST_PP_IIF BOOST_PP_IIF
When it should expand to
0,0 1,1 2,2 3,3
You can delay invoking your macro by first selecting it and then invoking it:
#define TEST(...)\
BOOST_PP_REPEAT( \
BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), \
MACRO, \
BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__))
#define MACRO(z, n, data) BOOST_PP_IF(1,MACRO_CONTAINING_COMMA,MACRO_CONTAINING_COMMA)(n, n)
#define MACRO_CONTAINING_COMMA(_NAME, _NAME2) _NAME EATEN_COMMA _NAME2
#define EATEN_COMMA BOOST_PP_IF(1,BOOST_PP_COMMA,BOOST_PP_TUPLE_EAT())()
See it work
The IF invocation expands to either your macro without an invocation or something that discard arguments when invoked. After one is chosen, the last parentheses invoke it with the desired arguments without the commas getting in the way.
Apart from that, I changed z to n and TIBRA_EATEN_COMMA() to EATEN_COMMA. As some parts are redundant, you can find a simpler version here.
It turns out that you can do this without __VA_ARGS__. In this simple example I used a comma which is in the template argument of function toString<int,int>() Working demo:
#include <boost/lexical_cast.hpp>
#include <boost/preprocessor.hpp>
#include <iostream>
#include <string>
#define SEQUENCE (1)(2)(3)(4)(5)(6)(7)(8)(9)(10)
#define IGNORE_ARG(arg)
#define GET_NAME(data) BOOST_PP_SEQ_ELEM(0, data)
#define GET_BELOW(data) BOOST_PP_SEQ_ELEM(1, data)
#define PARSE_SEQUENCE(r, data, elem) \
BOOST_PP_IF( \
BOOST_PP_GREATER_EQUAL(elem, GET_BELOW(data)), \
GET_NAME(data), IGNORE_ARG) \
(elem)
#define SKIP_NUMBERS_BELOW(name, below) \
BOOST_PP_SEQ_FOR_EACH(PARSE_SEQUENCE, (name)(below), SEQUENCE)
#define TEST(name) SKIP_NUMBERS_BELOW(name, 4)
#define MACRO_CONTAINING_COMMA(N) toString<N, 2 * N>() <<
template <int a, int b> // whatever, I just need a comma here.
std::string toString() {
return boost::lexical_cast<std::string>(a) + ":" + boost::lexical_cast<std::string>(b) + " ";
}
int main() {
std::cout << TEST(MACRO_CONTAINING_COMMA) "\n";
}

How can i generate variadic macro for concatenate string

I got stuck here...
#define CONCAT(a,b) BOOST_PP_STRINGIZE(BOOST_PP_CAT(a,b))#define CONCAT1(a,b,c) CONCAT(CONCAT(a,b),c) and so on. How i can to generate the CONCAT macro even if 20 arguments? May be i can to use BOOST_PP_SEQ_FOR_EACH but i don't understand how to do it?
It depends on you use-case.
This
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#define SEQ (a)(b)(c)
BOOST_PP_STRINGIZE(BOOST_PP_SEQ_CAT(SEQ)) // "abc"
will concatenate the sequence and then stringize it. It is also possible to simply stringize each argument as "a" "b" "c" is equivalent to "abc".
#define MY_STRINGIZE(r, data, elem) BOOST_PP_STRINGIZE(elem)
BOOST_PP_SEQ_FOR_EACH(MY_STRINGIZE, _, SEQ)
As you are already using the Boost libraries, try BOOST_PP_SEQ_CAT (documentation). It takes a list of elements and simply concatenates them together, i.e. BOOST_PP_SEQ_CAT(a b c)
results in abc.

Converting a MPL Vector to a Static Array

I wrote some code to generate a boost::mpl::vector to use as a lookup table for a factorial function, as a test for a more general library function with which a developer may be able to generate a lookup table in the form of a static array of primitives. The function (which would most probably be implemented as a preprocessor macro definition) would accept the name and size of the array to be initialized, as well the name of a class template to be used as the metafunction to initialize each element i of the array.
I thought that the best way to go about doing this without the use of external scripts would be to
Create a boost::mpl::vector, as is done in the code listing below, and push the return value of the user-supplied metafunction for each element of the array to the back of the vector;
Use the elements of the vector initialize the static array (perhaps by using a series of macros, the last of which would use the __VARARGS__ macro to accomplish this).
I know neither how I would accomplish (2) nor whether the procedure I describe is a good way of doing what I seek. Here are the following questions for which I would like answers:
Is my procedure a good way of accomplishing what I seek? If not, please describe a better procedure which would accomplish the same thing, without the use of external scripts.
If my procedure is indeed a good way of accomplishing what I seek, how would I implement (2)?
I will be sure to post a link to the source file containing library function which I describe once I implement it. The code listing follows below.
namespace mpl = boost::mpl;
template <typename x>
struct factorial:
mpl::if_<mpl::greater<x, mpl::int_<1>>,
mpl::multiplies<x, factorial<x::prior>>,
mpl::int_<1>
>::type
{};
template <typename sequence, typename size>
struct compileTable:
mpl::if_<mpl::greater<size, mpl::int_<0>>,
compileTable<
mpl::push_front<sequence, factorial<size>>::type,
size::prior
>,
sequence
>::type
{};
static const int TABLE_SIZE = 13;
typedef compileTable<
mpl::vector<>,
mpl::int_<TABLE_SIZE>
>::type factorialTable;
/*
** This is where I am stuck; how would I use the elements
** of factorialTable to initialize a static array?
*/
http://www.boost.org/doc/libs/1_46_0/libs/preprocessor/doc/index.html
#define MACRO(z, i, data) \
mpl::at_c<data,i>::value
static const data[] = { BOOST_PP_ENUM(N, MACRO, factorialTable) };
Here is the source code for the file containing the library function, as promised; please be sure to read the remarks I have made below the code listings. Thanks again to aaa for his help in showing me how to initialize a static array using BOOST_PP_ENUM!
Source code for xi/mpl/lut.h:
#ifndef __XI_LUT_INCLUDED__
#define __XI_LUT_INCLUDED__
#ifndef __cplusplus
#error The file __FILE__ requires a C++ compiler in order to be successfully compiled.
#endif
#include <boost/mpl/apply.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/greater.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#define __XI_LUT_SET_INDEX(z, n, sequence) \
mpl::at_c<sequence, n>::type::value
#define __XI_GENERATE_LUT_IMPL(function, tableType, tableName, tableSize) \
\
template <typename sequence, typename size> \
struct __compileTable_##function##_##tableSize##: \
mpl::if_<mpl::greater<size, mpl::int_<0>>, \
__compileTable_##function##_##tableSize##< \
mpl::push_front<sequence, \
mpl::apply< \
function##<mpl::_>, \
size \
>::type>::type, \
size::prior \
>, \
sequence \
>::type \
{}; \
\
typedef __compileTable_##function##_##tableSize##< \
mpl::vector<>, \
mpl::int_<##tableSize##> \
>::type __compiledTable_##function##_##tableSize##; \
\
static const tableType tableName##[] = { \
BOOST_PP_ENUM( \
tableSize##, \
__XI_LUT_SET_INDEX, \
__compiledTable_##function##_##tableSize## \
) \
}
#define XI_GENERATE_LUT(function, tableType, tableName, tableSize) \
__XI_GENERATE_LUT_IMPL(function, tableType, tableName, tableSize)
#endif
Source code for a useful test file:
#include <boost/mpl/greater.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/placeholders.hpp>
#include <cstdio>
#include <xi/mpl/lut.hpp>
namespace mpl = boost::mpl;
template <typename x>
struct factorial:
mpl::if_<mpl::greater<x, mpl::int_<1>>,
mpl::multiplies<x, factorial<x::prior>>,
mpl::int_<1>
>::type
{};
XI_GENERATE_LUT(factorial, int, FACTORIAL_TABLE, 4);
int main(int argc, char ** argv) {
// This should print '24:'
printf("Result: %d.\n", FACTORIAL_TABLE[3]);
return 0;
}
I will refrain from providing a URL to the file for now so that I can continue to edit the code listing. I am confident that the code can be improved for purposes of compatibility, so it is definitely not in a final state. Here are some known issues:
The code will not compile on MSVC 9.0.
Attempting to create a lookup table of a particular size for a metafunction name after one has already been created of the same size and for the same metafunction name will result in an error, since corresponding types and templates would be defined for these parameters. I do not want to use __COUNTER__ to alleviate this problem since it is a nonstandard macro definition.
I have not tried compiling this code on any other compilers except ICC and MSCV, and would like to know how GCC handles it - please let me know of any issues which arise so that proper recourse may be taken. I will post a URL to the file once the code works with little trouble on most major compilers. Any feedback would be greatly appreciated!

Syntax Error in Preprocessor Macro Code

I am trying to write some code for a macro which returns the length of a string, and am attempting to implement it using BOOST_PP_WHILE. The code stems from the fact that a character at a position specified by position of the string represented by a macro argument foo may be obtained by #foo[position]. Compiling using either MSVC or Intel C++ results in similar syntax errors; if you could point out why the code is generating these syntax errors and how I would rectify code, it would be greatly appreciated. I know that the errors are caused by the code within the PREDICATE macro, but any expression I attempt to use within it barring BOOST_PP_TUPLE_ELEM results in a compile-time error.
Errors:
prog.cpp:47:1: error: pasting "BOOST_PP_BOOL_" and ""\"Hello, World!\""" does not give a valid preprocessing token
prog.cpp: In function ‘int main(int, char**)’:
prog.cpp:47: error: ‘BOOST_PP_TUPLE_ELEM_2_1’ was not declared in this scope
As one would expect, the line numbers are not very useful since both point to the line at which the macro MACRO_STRLEN is called.
Code
Below follows the source listing in which I attempt to implement the macro which I describe.
#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/control/while.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <cstdio>
#define TEST_STRING0 "Hello, World!"
#define MACRO_IS_NULL_IMPL(x, position) \
#x[position] == '\0'
#define MACRO_IS_NULL(x, position) \
MACRO_IS_NULL_IMPL(x, position)
#define PREDICATE_D(string, position) \
MACRO_IS_NULL(string, position)
#define PREDICATE(n, state) \
PREDICATE_D( \
BOOST_PP_TUPLE_ELEM(2, 0, state), \
BOOST_PP_TUPLE_ELEM(2, 1, state) \
)
#define OPERATION_D(string, position) \
( \
string, \
BOOST_PP_INC(position) \
)
#define OPERATION(d, state) \
OPERATION_D( \
BOOST_PP_TUPLE_ELEM(2, 0, state), \
BOOST_PP_TUPLE_ELEM(2, 1, state) \
)
#define MACRO_STRLEN_IMPL(string) \
BOOST_PP_TUPLE_ELEM( \
2, 1, BOOST_PP_WHILE(PREDICATE, OPERATION, (string, 0)) \
)
#define MACRO_STRLEN(string) \
MACRO_STRLEN_IMPL(string)
int main(int argc, char ** argv) {
printf("String length: %d.\n", MACRO_STRLEN(TEST_STRING0));
return 0;
}
How about this - http://codepad.org/aT7SK1Lu
Its still a compile-time strlen, and would be likely much faster to compile.
#include <stdio.h>
#include <string.h>
#define TEST_STRING "Hello, World!"
template <int N> struct xtmp2 { typedef char (&t)[N]; };
template< class T, int N > typename xtmp2<N>::t xlen( T (&)[N] );
#define STRLEN(x) (sizeof(xlen(x))-1)
int main( void ) {
printf( "strlen(\"%s\") = %i %i\n", TEST_STRING, STRLEN(TEST_STRING), strlen(TEST_STRING) );
}
As to macro debug, its possible to get a preprocessor output (like gcc -E);
it may be also helpful to undefine most macros, then enable them one by one to
see what happens.
Please forgive me if this is an irrelevant pointing out.
The predicate for BOOST_PP_WHILE is evaluated while preprocess.
However, if I understand correctly, MACRO_IS_NULL_IMPL determines whether
the character is '\0' at compile-time(runtime?).
So, I think it is difficult to accomplish the goal directly with string
literal "Hello, World!".
It won't work, and for a simple reason: the preprocessor is not meant to deal with literals.
The preprocessor only knows about "tokens", it can catenate them, it can transform one into a string literal, and it can operate macros replacements, but that's it.
Here, the condition to stop the loop (use of [] and ==) could be, at best, executed by the compiler (and most likely at runtime), therefore is not suitable for BOOST_PP_WHILE.
You can, actually, use the compiler to get the number of elements of an array (here an array of characters):
For example using sizeof: sizeof(array)/sizeof(array[0]). This can be abstracted in a macro, however it cannot become a "regular" function, since arrays cannot be passed to "regular" functions, only pointers (where you've lost the information size).
You can also use a template function:
template <typename T, size_t N>
size_t size(T (&)[N]) { return N; }
(this actually work on any array with a constant size)
But, for your own issue, you'll be pleased to know that most compilers have a built-in strlen implementation for constants that evaluates at compile-time.
I wonder if it was supposed to be something like this:
#include <stdio.h>
#include <string.h>
#define TEST_STRING "Hello, World!"
#define STRLEN(x) (x[0]==0)?0:TEST_01(x,1)
#define TEST_01(x,y) (x[y]==0)?y:TEST_02(x,y+1)
#define TEST_02(x,y) (x[y]==0)?y:TEST_03(x,y+1)
#define TEST_03(x,y) (x[y]==0)?y:TEST_04(x,y+1)
#define TEST_04(x,y) (x[y]==0)?y:TEST_05(x,y+1)
#define TEST_05(x,y) (x[y]==0)?y:TEST_06(x,y+1)
#define TEST_06(x,y) (x[y]==0)?y:TEST_07(x,y+1)
#define TEST_07(x,y) (x[y]==0)?y:TEST_08(x,y+1)
#define TEST_08(x,y) (x[y]==0)?y:TEST_09(x,y+1)
#define TEST_09(x,y) (x[y]==0)?y:TEST_10(x,y+1)
#define TEST_10(x,y) (x[y]==0)?y:TEST_11(x,y+1)
#define TEST_11(x,y) (x[y]==0)?y:TEST_12(x,y+1)
#define TEST_12(x,y) (x[y]==0)?y:TEST_13(x,y+1)
#define TEST_13(x,y) (x[y]==0)?y:TEST_14(x,y+1)
#define TEST_14(x,y) (x[y]==0)?y:TEST_15(x,y+1)
#define TEST_15(x,y) (x[y]==0)?y:TEST_16(x,y+1)
#define TEST_16(x,y) (x[y]==0)?y:TEST_17(x,y+1)
#define TEST_17(x,y) (x[y]==0)?y:TEST_18(x,y+1)
#define TEST_18(x,y) (x[y]==0)?y:TEST_19(x,y+1)
#define TEST_19(x,y) (x[y]==0)?y:-1
int main( void ) {
printf( "strlen(\"%s\") = %i %i\n", TEST_STRING, STRLEN(TEST_STRING), strlen(TEST_STRING) );
}
But this isn't a compile-time evaluation, even though it would be usually
optimized to a constant.