Search code examples
c++boostc-preprocessortemplate-specializationboost-preprocessor

Generate a set of tagged and indexed specializations of a class template using Boost.Preprocessor


There is a need to create a macro to generate a set of overloadings of a struct specialized by tag and index. I tried the following:

#include <boost/preprocessor/seq/for_each_i.hpp>

template< typename /*tag*/, int /*index*/ >
struct S;

#define GEN(ignored, tilda, index, type_name) \
    template<> struct S< struct BOOST_PP_SEQ_TAIL(type_name), index > \
    { BOOST_PP_SEQ_HEAD(type_name) BOOST_PP_SEQ_TAIL(type_name); };

#if 1
BOOST_PP_SEQ_FOR_EACH_I(GEN, ~, ((int)(i)))
#else
// above should be equiv to:
template<> struct S< struct i, 0 > { int i; };
#endif

int main()
{
    S< i, 0 >{}.i; // check if accessible
}

But get an error:

prog.cc:8:111: error: wrong number of template arguments (1, should be 2)
 #define GEN(ignored, tilda, index, type_name) template<> struct S< struct BOOST_PP_SEQ_TAIL(type_name), index > { BOOST_PP_SEQ_HEAD(type_name) BOOST_PP_SEQ_TAIL(type_name); };
                                                                                                               ^
/usr/local/boost-1.62.0/include/boost/preprocessor/seq/for_each_i.hpp:85:66: note: in expansion of macro 'GEN'
 # define BOOST_PP_SEQ_FOR_EACH_I_M_I(r, macro, data, seq, i, sz) macro(r, data, i, BOOST_PP_SEQ_HEAD(seq))
                                                                  ^~~~~
/usr/local/boost-1.62.0/include/boost/preprocessor/seq/for_each_i.hpp:80:49: note: in expansion of macro 'BOOST_PP_SEQ_FOR_EACH_I_M_I'
 #    define BOOST_PP_SEQ_FOR_EACH_I_M_IM(r, im) BOOST_PP_SEQ_FOR_EACH_I_M_I(r, im)
                                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/boost-1.62.0/include/boost/preprocessor/seq/for_each_i.hpp:79:45: note: in expansion of macro 'BOOST_PP_SEQ_FOR_EACH_I_M_IM'
 #    define BOOST_PP_SEQ_FOR_EACH_I_M(r, x) BOOST_PP_SEQ_FOR_EACH_I_M_IM(r, BOOST_PP_TUPLE_REM_5 x)
                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/boost-1.62.0/include/boost/preprocessor/seq/for_each_i.hpp:79:77: note: in expansion of macro 'BOOST_PP_TUPLE_REM_5'
 #    define BOOST_PP_SEQ_FOR_EACH_I_M(r, x) BOOST_PP_SEQ_FOR_EACH_I_M_IM(r, BOOST_PP_TUPLE_REM_5 x)
                                                                             ^~~~~~~~~~~~~~~~~~~~
/usr/local/boost-1.62.0/include/boost/preprocessor/control/iif.hpp:32:31: note: in expansion of macro 'BOOST_PP_SEQ_FOR_EACH_I_M'
 # define BOOST_PP_IIF_1(t, f) t
                               ^
/usr/local/boost-1.62.0/include/boost/preprocessor/repetition/detail/for.hpp:22:37: note: in expansion of macro 'BOOST_PP_FOR_1_C'
 # define BOOST_PP_FOR_1(s, p, o, m) BOOST_PP_FOR_1_C(BOOST_PP_BOOL(p(2, s)), s, p, o, m)
                                     ^~~~~~~~~~~~~~~~
/usr/local/boost-1.62.0/include/boost/preprocessor/cat.hpp:29:34: note: in expansion of macro 'BOOST_PP_FOR_1'
 #    define BOOST_PP_CAT_I(a, b) a ## b
                                  ^
/usr/local/boost-1.62.0/include/boost/preprocessor/control/iif.hpp:32:31: note: in expansion of macro 'BOOST_PP_SEQ_FOR_EACH_I_DETAIL_CHECK_EXEC'
 # define BOOST_PP_IIF_1(t, f) t
                               ^
/usr/local/boost-1.62.0/include/boost/preprocessor/seq/for_each_i.hpp:30:55: note: in expansion of macro 'BOOST_PP_SEQ_FOR_EACH_I_DETAIL_CHECK'
 #    define BOOST_PP_SEQ_FOR_EACH_I(macro, data, seq) BOOST_PP_SEQ_FOR_EACH_I_DETAIL_CHECK(macro, data, seq)
                                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:10:1: note: in expansion of macro 'BOOST_PP_SEQ_FOR_EACH_I'
 BOOST_PP_SEQ_FOR_EACH_I(GEN, ~, ((int)(i)))
 ^~~~~~~~~~~~~~~~~~~~~~~
prog.cc:6:8: note: provided for 'template<class, long unsigned int <anonymous> > struct S'
 struct S;
        ^
prog.cc: In function 'int main()':
prog.cc:16:8: error: 'i' was not declared in this scope
     S< i, 0 >{}.i; // check if accessible
        ^
prog.cc:16:13: error: template argument 1 is invalid
     S< i, 0 >{}.i; // check if accessible
             ^

It seems, that struct BOOST_PP_SEQ_TAIL(type_name) part of macro cannot be handled correctly, but why? If I replace first occurrence of BOOST_PP_SEQ_TAIL(type_name) with i, then code compiles fine.

What is the source of an error?


Solution

  • I removed the #include <cstdint> for brevity's sake, but try compiling with -P -E which will output just the result of the preprocessor run, which is quite valuable when debugging Boost.PP programs.

    The output of that is:

    template< typename , int >
    struct S;
    template<> struct S< struct (i), 0 > { int (i); };
    int main()
    {
        S< i, 0 >{}.i;
    }
    

    from which the error is immediately obvious: struct (i) and int (i) are invalid syntax.

    This answer might help you with what to do about that.

    Here's my solution to this, which is to create my own little tuple-accessing macros:

    #include <boost/preprocessor/seq/for_each_i.hpp>
    
    template< typename /*tag*/, int /*index*/ >
    struct S;
    
    #define FIRST(a, b) a
    #define SECOND(a, b) b
    
    #define GEN(ignored, tilda, index, type_name)                           \
        template<> struct S< struct SECOND type_name, index > \
        { FIRST type_name SECOND type_name; };
    
    #if 1
    BOOST_PP_SEQ_FOR_EACH_I(GEN, ~, ((int, i)))
    #else
    // above should be equiv to:
    template<> struct S< struct i, 0 > { int i; };
    #endif
    
    int main()
    {
        S< i, 0 >{}.i; // check if accessible
    }