[preprocessor] repeating expressions containing multiple macros

Hello, I would like to propose an extension to the Preprocessor library, based on the REPEAT, ENUM etc. family of macros, e.g., BOOST_PP_REPEAT(count, macro, data) macro which expands to: macro(z, 0, data) macro(z, 1, data) ... macro(z, count - 1, data) In working on the RPC and Signal Network libraries, I found useful being able to iterate over multiple macros simultaneously. This could be provided by a set of macros BOOST_PP_REPEAT_(C|M|MA)+ where C stands for Constant, M for Macro, and MA for Macro+Argument. For example, BOOST_PP_REPEAT_CMAMC(count, cons, macro, arg, macro2, cons2) expands to (linebreaks inserted for readability): cons macro(z, 0, arg) macro2(z, 0, BOOST_PP_EMPTY()) cons2 cons macro(z, 1, arg) macro2(z, 1, BOOST_PP_EMPTY()) cons2 ... cons macro(z, count-1, arg) macro2(z, count-1, BOOST_PP_EMPTY()) cons2 This particular macro would be implemented as #define BOOST_PP_CMAMC_MACRO(z, n, args) \ BOOST_PP_TUPLE_ELEM(5, 0, args) \ BOOST_PP_TUPLE_ELEM(5, 1, args)(z,n,BOOST_PP_TUPLE_ELEM(5, 2, args)) \ BOOST_PP_TUPLE_ELEM(5, 3, args)(z,n,BOOST_PP_EMPTY()) \ BOOST_PP_TUPLE_ELEM(5, 4, args) #define BOOST_PP_REPEAT_CMAMC(count, c1, m2, a3, m4, c5) \ BOOST_PP_REPEAT(count, BOOST_PP_CMAMC_MACRO, (c1, m2, a3, m4, c5)) As a usage example, I am finding such expansions useful in developing classes / functions in which an integer template parameter determines the number of arguments to a function/method and/or the number of local/class variables (for example, when the function signature is determined by a signature provided as a template parameter, and the function needs a local variable for each parameter). In this case, I iterate over the a template implementation using BOOST_PP_ITERATE and have a set of macros equivalent to the ones above, except they hard-code the count to BOOST_PP_ITERATION() (they are called BOOST_ARITY_* instead of BOOST_PP_*). Take as an example a function "serialize" which is templated on a function Signature, where the arguments of the function are specified by the signature. For example, std::string serialized_params; serialized_params = serialize<void(int, float, std::string)>(1, 2.5, "serialize this"); The above macros make the following implementation a little more readable than other alternatives I've come accross (what follows is adapted from working code and untested): --- first some prep work, so we have macros to iterate over: // define names a1, a2, ... #define ARITY_aN_NAME(z,n,data) \ BOOST_PP_CAT(a,BOOST_PP_INC(n)) // define names arg1_type, arg2_type, ...: #define ARITY_argN_type_NAME(z,n,data) \ BOOST_PP_CAT(BOOST_PP_CAT(arg,BOOST_PP_INC(n)),_type) // define a way to get to the type of an argument, given the signature: #define ARITY_function_traits_argN_type_NAME(z,n,signature) \ typename boost::function_traits<signature>:: \ ARITY_argN_type_NAME(z,n,BOOST_PP_EMPTY()) // just some shortcuts: #define BOOST_ARITY_ENUM_MAM(m1, a2, m3) \ BOOST_PP_ENUM_MAM(BOOST_PP_ITERATION(), m1, m2, m3) #define BOOST_ARITY_REPEAT_CMC(c1, m2, c3) \ BOOST_PP_REPEAT_CMC(BOOST_PP_ITERATION(), c1, m2, c3) ---- the following part is iterated over using BOOST_PP_ITERATE: // define a function which serializes its arguments, and the // number and type of arguments is specified by a template param: template<typename Signature> // retuns a string typename boost::enable_if_c<boost::function_traits<Signature>::arity ==BOOST_PP_ITERATION(), std::string>::type // the list of arguments is expanded through the macros: // (typename boost::function_traits<Signature>::arg1_type a1, // typename boost::function_traits<Signature>::arg2_type a2, ...) serialize(BOOST_ARITY_ENUM_MAM(ARITY_function_traits_argN_type_NAME, Signature, ARITY_aN_NAME)) { std::stringstream stream(std::ios::in | std::ios::out | std::ios::binary); boost::archive::binary_oarchive archive(stream); // archive & a1; archive & a2; ... BOOST_ARITY_REPEAT_CMC(archive &, ARITY_aN_NAME, ;) return stream.str(); } --- The BOOST_ARITY_* macros above are easily implemented using BOOST_PP_* macros, and are just one usage example. Macros of the type BOOST_PP_REPEAT_(C|M|MA)+ could probably have other applications as well. If there isn't functionality which already covers this, would there be any interest in adding the BOOST_PP_REPEAT_(C|M|MA)+ etc. macros to the preprocessor library? If so, I could write up a python script which would generate the macros for up to a reasonable number of arguments (e.g., four C, M, or MAs which would lead to 3^4 base macros plus the same number of BOOST_PP_REPEAT_*, BOOST_PP_EVAL_*, and any other macros that should be included). Regards, Stjepan

Sorry, never mind :-) - Tobias pointed me to fusion and function_types::parameter_types, which looks like a much better way to do what I'm trying to accomplish anyway (and he also pointed out a nicer way to do the macro expansion I was proposing using what's already in PP) Apologies for the noise! Stjepan On 5/30/07, Stjepan Rajko <stipe@asu.edu> wrote:
Hello,
I would like to propose an extension to the Preprocessor library, based on the REPEAT, ENUM etc. family of macros, e.g., BOOST_PP_REPEAT(count, macro, data) macro which expands to:
macro(z, 0, data) macro(z, 1, data) ... macro(z, count - 1, data)
In working on the RPC and Signal Network libraries, I found useful being able to iterate over multiple macros simultaneously. This could be provided by a set of macros BOOST_PP_REPEAT_(C|M|MA)+ where C stands for Constant, M for Macro, and MA for Macro+Argument. For example,
BOOST_PP_REPEAT_CMAMC(count, cons, macro, arg, macro2, cons2) expands to (linebreaks inserted for readability):
cons macro(z, 0, arg) macro2(z, 0, BOOST_PP_EMPTY()) cons2 cons macro(z, 1, arg) macro2(z, 1, BOOST_PP_EMPTY()) cons2 ... cons macro(z, count-1, arg) macro2(z, count-1, BOOST_PP_EMPTY()) cons2
This particular macro would be implemented as
#define BOOST_PP_CMAMC_MACRO(z, n, args) \ BOOST_PP_TUPLE_ELEM(5, 0, args) \ BOOST_PP_TUPLE_ELEM(5, 1, args)(z,n,BOOST_PP_TUPLE_ELEM(5, 2, args)) \ BOOST_PP_TUPLE_ELEM(5, 3, args)(z,n,BOOST_PP_EMPTY()) \ BOOST_PP_TUPLE_ELEM(5, 4, args)
#define BOOST_PP_REPEAT_CMAMC(count, c1, m2, a3, m4, c5) \ BOOST_PP_REPEAT(count, BOOST_PP_CMAMC_MACRO, (c1, m2, a3, m4, c5))
As a usage example, I am finding such expansions useful in developing classes / functions in which an integer template parameter determines the number of arguments to a function/method and/or the number of local/class variables (for example, when the function signature is determined by a signature provided as a template parameter, and the function needs a local variable for each parameter). In this case, I iterate over the a template implementation using BOOST_PP_ITERATE and have a set of macros equivalent to the ones above, except they hard-code the count to BOOST_PP_ITERATION() (they are called BOOST_ARITY_* instead of BOOST_PP_*).
Take as an example a function "serialize" which is templated on a function Signature, where the arguments of the function are specified by the signature. For example,
std::string serialized_params; serialized_params = serialize<void(int, float, std::string)>(1, 2.5, "serialize this");
The above macros make the following implementation a little more readable than other alternatives I've come accross (what follows is adapted from working code and untested):
--- first some prep work, so we have macros to iterate over:
// define names a1, a2, ... #define ARITY_aN_NAME(z,n,data) \ BOOST_PP_CAT(a,BOOST_PP_INC(n))
// define names arg1_type, arg2_type, ...: #define ARITY_argN_type_NAME(z,n,data) \ BOOST_PP_CAT(BOOST_PP_CAT(arg,BOOST_PP_INC(n)),_type)
// define a way to get to the type of an argument, given the signature: #define ARITY_function_traits_argN_type_NAME(z,n,signature) \ typename boost::function_traits<signature>:: \ ARITY_argN_type_NAME(z,n,BOOST_PP_EMPTY())
// just some shortcuts: #define BOOST_ARITY_ENUM_MAM(m1, a2, m3) \ BOOST_PP_ENUM_MAM(BOOST_PP_ITERATION(), m1, m2, m3)
#define BOOST_ARITY_REPEAT_CMC(c1, m2, c3) \ BOOST_PP_REPEAT_CMC(BOOST_PP_ITERATION(), c1, m2, c3)
---- the following part is iterated over using BOOST_PP_ITERATE:
// define a function which serializes its arguments, and the // number and type of arguments is specified by a template param:
template<typename Signature>
// retuns a string typename boost::enable_if_c<boost::function_traits<Signature>::arity ==BOOST_PP_ITERATION(), std::string>::type
// the list of arguments is expanded through the macros: // (typename boost::function_traits<Signature>::arg1_type a1, // typename boost::function_traits<Signature>::arg2_type a2, ...) serialize(BOOST_ARITY_ENUM_MAM(ARITY_function_traits_argN_type_NAME, Signature, ARITY_aN_NAME)) { std::stringstream stream(std::ios::in | std::ios::out | std::ios::binary); boost::archive::binary_oarchive archive(stream);
// archive & a1; archive & a2; ... BOOST_ARITY_REPEAT_CMC(archive &, ARITY_aN_NAME, ;) return stream.str(); }
---
The BOOST_ARITY_* macros above are easily implemented using BOOST_PP_* macros, and are just one usage example. Macros of the type BOOST_PP_REPEAT_(C|M|MA)+ could probably have other applications as well.
If there isn't functionality which already covers this, would there be any interest in adding the BOOST_PP_REPEAT_(C|M|MA)+ etc. macros to the preprocessor library? If so, I could write up a python script which would generate the macros for up to a reasonable number of arguments (e.g., four C, M, or MAs which would lead to 3^4 base macros plus the same number of BOOST_PP_REPEAT_*, BOOST_PP_EVAL_*, and any other macros that should be included).
Regards,
Stjepan
participants (1)
-
Stjepan Rajko