Variable Preprocess Metaprogramming?

As we known, there is a preprocess metaprogramming library in boost, but it is not flexible enough. we are unable to define an custom preprocess loop(something like #for or #while ?), and we are also unable to use recursion. I have an idea to do that, C/C++ allows recursion of #include, so we can put the codes we want to evaluate repeatedly to a head, and include itself depends on a preprocess condition. But if we wrote code below, we would meet a error of macro redefinition. /**** foo.hpp ****/ #if FOO < 10 // do something with I # define FOO FOO + 1 # include "foo.hpp" #endif /**** foo.hpp ****/ /**** bar.hpp ****/ #define FOO 0 #include "foo.hpp" /**** bar.hpp ****/ The problem is that an macro must not depends on itself. Thus, to change a preprocess variant, I use a bit of #if to transfer value of a macro to some temporary macro. see my code below: /**** begin_set_value.hpp ****/ #if (BOOST_VPP_VALUE & (1 << 0)) == 0 #define BOOST_VPP_TEMPORARY_BIT_0 0 #else #define BOOST_VPP_TEMPORARY_BIT_0 1 #endif #if (BOOST_VPP_VALUE & (1 << 1)) == 0 #define BOOST_VPP_TEMPORARY_BIT_1 0 #else #define BOOST_VPP_TEMPORARY_BIT_1 1 #endif /* ... */ #if (BOOST_VPP_VALUE & (1 << 30)) == 0 #define BOOST_VPP_TEMPORARY_BIT_30 0 #else #define BOOST_VPP_TEMPORARY_BIT_30 1 #endif #if (BOOST_VPP_VALUE & (1 << 31)) == 0 #define BOOST_VPP_TEMPORARY_BIT_31 0 #else #define BOOST_VPP_TEMPORARY_BIT_31 1 #endif #undef BOOST_VPP_VALUE #define BOOST_VPP_VALUE \ (BOOST_VPP_TEMPORARY_BIT_0 << 0 ) \ + (BOOST_VPP_TEMPORARY_BIT_1 << 1 ) \ + (BOOST_VPP_TEMPORARY_BIT_2 << 2 ) \ + (BOOST_VPP_TEMPORARY_BIT_3 << 3 ) \ /* ... */ \ + (BOOST_VPP_TEMPORARY_BIT_29 << 29 ) \ + (BOOST_VPP_TEMPORARY_BIT_30 << 30 ) \ + (BOOST_VPP_TEMPORARY_BIT_31 << 31 ) \ /*BOOST_VPP_VALUE*/ /**** begin_set_value.hpp ****/ Now, the BOOST_VPP_VALUE is depends on some of BOOST_VPP_TEMPORARY_BIT_n, and can be used to change the former macro: #define FOO 0 #define BOOST_VPP_VALUE FOO #include "begin_set_value.hpp" #undef FOO #define FOO (BOOST_VPP_VALUE + 1) But, FOO depends on BOOST_VPP_TEMPORARY_BIT_ns, and can not be change again, the solution is that A must a variant depends on some other BIT_n definition instead of directly depends on BOOST_VPP_VALUE. Actually, I implements an variant stack to allocate and release these variant. I think the concept of variable preprocess is useful, if Boost.Typeof or Boost.Bind use that, these code they need would be less than copying same codes. I have upload a sample on vault, file name is variable_preprocess.zip. http://boost-consulting.com/vault/index.php?&direction=0&order=&directory=Preprocessor%20Metaprogramming http://boost-consulting.com/vault/index.php?action=downloadfile&filename=variable_preprocess.zip&directory=Preprocessor%20Metaprogramming& All code is validated by wave, thank you for your attention.

On Apr 14, 2007, at 8:10 AM, Atry wrote:
I think the concept of variable preprocess is useful, if Boost.Typeof or Boost.Bind use that, these code they need would be less than copying same codes.
Or you could just use variadic templates, which eliminate the preprocessor entirely and are likely to be included in C++0x. http://www.osl.iu.edu/~dgregor/cpp/variadic-templates.html - Doug

"Douglas Gregor" <doug.gregor@gmail.com> wrote
I think the concept of variable preprocess is useful, if Boost.Typeof or Boost.Bind use that, these code they need would be less than copying same codes.
Or you could just use variadic templates, which eliminate the preprocessor entirely and are likely to be included in C++0x.
I think that a compiler that supports variadic templates would most likely support typeof directly, so I doubt typeof emulation will ever benefit from this cool feature :) Regards, Arkadiy

I gave a glance to the papers about variadic templates and I didn't see examples using a construct such as f(args)... where args is a function parameter pack; so I guess that the following code it's illegal: template< typename T > T max( T x ) { return x; } template< typename T, typename... Args > // where are_same<T, Args...> T max( T x, T y, Args... args ) { return std::max( x, max( y, args... ) ); } // #1 template< typename T, typename... Args > // where are_same<T, Args...> T max_abs( T x, T y, Args... args ) { // this is the (probably) illegal construct: return max( abs(x), abs(y), abs(args)... ); // I'd like that "abs(args)..." expands to "abs(arg1), ... ,abs(argN)" } What's the rationale behind such a design choice ? I admit that a simple workaround exists: // #2 template< typename T > T max_abs( T x ) { return abs(x); } template< typename T, typename... Args > // where are_same<T, Args...> T max_abs( T x, T y, Args... args ) { return std::max(abs(x), max_abs(y, args...) ); } anyway version #1 is more straightforward, and maybe it's possible to make up examples more complex than mine. Another question: given the following functions: // #1 template< typename T > void f(T ) {} // #2 template< typename T, typename... Args > void f(T, Args... ) {} the function call f(1,2) is it ambiguous ? if it isn't, which is the function called ? #1 because it's more specialized ? (only a guess) Kind Regards, Marco -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/

On Mon, 16 Apr 2007 16:33:23 +0200, Marco <mrcekets@gmail.com> wrote:
Another question: given the following functions:
// #1 template< typename T > void f(T ) {}
// #2 template< typename T, typename... Args > void f(T, Args... ) {}
the function call f(1,2) is it ambiguous ? if it isn't, which is the function called ? #1 because it's more specialized ? (only a guess)
sorry, I mean f(1), obviously f(1,2) it's not ambiguous at all ! Marco -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/

Hello, On Apr 16, 2007, at 3:33 PM, Marco wrote:
I gave a glance to the papers about variadic templates and I didn't see examples using a construct such as f(args)... where args is a function parameter pack; so I guess that the following code it's illegal:
The specification of variadic templates is the authority on what can and can't be done with them. The compiler, and the examples in the papers, just help to illustrate some of the capabilities. In this case...
template< typename T > T max( T x ) { return x; }
template< typename T, typename... Args > // where are_same<T, Args...> T max( T x, T y, Args... args ) { return std::max( x, max( y, args... ) ); }
// #1 template< typename T, typename... Args > // where are_same<T, Args...> T max_abs( T x, T y, Args... args ) { // this is the (probably) illegal construct: return max( abs(x), abs(y), abs(args)... ); // I'd like that "abs(args)..." expands to "abs(arg1), ... ,abs (argN)" }
Sure, that's fine.
What's the rationale behind such a design choice ?
It's a great idea, so we did it :)
I admit that a simple workaround exists:
// #2 template< typename T > T max_abs( T x ) { return abs(x); }
template< typename T, typename... Args > // where are_same<T, Args...> T max_abs( T x, T y, Args... args ) { return std::max(abs(x), max_abs(y, args...) ); }
anyway version #1 is more straightforward, and maybe it's possible to make up examples more complex than mine.
The implementations of Bind, Function, and Tuple involve much more complex uses of variadic templates.
Another question: given the following functions:
// #1 template< typename T > void f(T ) {}
// #2 template< typename T, typename... Args > void f(T, Args... ) {}
the function call f(1,2) is it ambiguous ? if it isn't, which is the function called ? #1 because it's more specialized ? (only a guess)
For f(1, 2), only #2 matches. For f(1), both #1 and #2 match, but #1 is more specialized. - Doug

On Mon, 16 Apr 2007 23:08:42 +0200, Douglas Gregor <doug.gregor@gmail.com> wrote:
Hello,
On Apr 16, 2007, at 3:33 PM, Marco wrote:
I gave a glance to the papers about variadic templates and I didn't see examples using a construct such as f(args)... where args is a function parameter pack; so I guess that the following code it's illegal:
The specification of variadic templates is the authority on what can and can't be done with them. The compiler, and the examples in the papers, just help to illustrate some of the capabilities. In this case...
template< typename T > T max( T x ) { return x; }
template< typename T, typename... Args > // where are_same<T, Args...> T max( T x, T y, Args... args ) { return std::max( x, max( y, args... ) ); }
// #1 template< typename T, typename... Args > // where are_same<T, Args...> T max_abs( T x, T y, Args... args ) { // this is the (probably) illegal construct: return max( abs(x), abs(y), abs(args)... ); // I'd like that "abs(args)..." expands to "abs(arg1), ... ,abs (argN)" }
Sure, that's fine.
What's the rationale behind such a design choice ?
It's a great idea, so we did it :)
Great! I had missed that patterned pack expansion works with function parameter packs too. I try to dare something more, now. Has it been taken in consideration to provide a typedef type pack defined throught a template parameter pack ? That is, something like: template< typename... Args > class { typedef typename Args::value_type value_types; // or the following more explicit syntax: // typedef... typename Args::value_type value_types; void f( value_types... args ); }; Regards, Marco Using Opera's revolutionary e-mail client: http://www.opera.com/mail/

in reply to myself ...
Has it been taken in consideration to provide a typedef type pack defined throught a template parameter pack ?
trying to be clearer what I'm speaking about it's a way to define a name for a pattern of a pack expansion that expands to a sequence of types. Marco -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/

On Apr 17, 2007, at 3:42 PM, Marco wrote:
I try to dare something more, now. Has it been taken in consideration to provide a typedef type pack defined throught a template parameter pack ? That is, something like:
template< typename... Args > class { typedef typename Args::value_type value_types; // or the following more explicit syntax: // typedef... typename Args::value_type value_types;
void f( value_types... args );
};
Yes, it's been taken into consideration, but it does not work. - Doug

Hello, On 17/04/2007, Douglas Gregor <doug.gregor@gmail.com> wrote:
On Apr 17, 2007, at 3:42 PM, Marco wrote:
I try to dare something more, now. Has it been taken in consideration to provide a typedef type pack defined throught a template parameter pack ? That is, something like:
template< typename... Args > class { typedef typename Args::value_type value_types; // or the following more explicit syntax: // typedef... typename Args::value_type value_types;
void f( value_types... args );
};
Yes, it's been taken into consideration, but it does not work.
Sorry to resurrect an old thread, but I was wondering why this doesn't work and whether it's been changed? Regards and thanks, Peter Bindels

On Sep 20, 2007, at 12:59 PM, Peter Bindels wrote:
Hello, On 17/04/2007, Douglas Gregor <doug.gregor@gmail.com> wrote:
On Apr 17, 2007, at 3:42 PM, Marco wrote:
I try to dare something more, now. Has it been taken in consideration to provide a typedef type pack defined throught a template parameter pack ? That is, something like:
template< typename... Args > class { typedef typename Args::value_type value_types; // or the following more explicit syntax: // typedef... typename Args::value_type value_types;
void f( value_types... args );
};
Yes, it's been taken into consideration, but it does not work.
Sorry to resurrect an old thread, but I was wondering why this doesn't work and whether it's been changed?
It has not and will not be changed. It doesn't work because one would not be able to know when a dependent name is actually a parameter pack. Here's a short example: // This code is ill-formed template<typename... Types> struct Foo { typedef Types... types; }; struct Bar { typedef Bar types; }; template<typename T> struct Storage { typename T::types values; // problem: is T::types a parameter pack or not? } What is typename T::types, a type or a parameter pack of types? We absolutely have to know when the process the template whether this construct is a parameter pack or not, because it affects how pack expansions are processed. However, instantiating Storage with Foo<int, float> makes T::types a parameter pack, while instantiating Storage with Bar makes T::types a single type. The "fix" for this is to do the same thing that "typename" does for "typename T::foo"... "typename" was added because the compiler can't tell whether T::foo is a a value or a type. We would need something like: typename... T::types to that say T::types is a type parameter pack. We'd need the same thing for value parameters packs and anything else that could be a parameter pack. It's not impossible to introduce such an extension, but doing so drastically increases the syntactic cost of variadic templates, and the addition of yet new things to worry about with dependent names makes variadic templates harder to use. For all these additions, you don't actually gain any expressive power: tuple<Types...> values; does basically the same thing as the requested functionality.\ - Doug

On 20/09/2007, Doug Gregor <dgregor@osl.iu.edu> wrote:
It has not and will not be changed.
It doesn't work because one would not be able to know when a dependent name is actually a parameter pack. Here's a short example:
What is typename T::types, a type or a parameter pack of types? We absolutely have to know when the process the template whether this construct is a parameter pack or not, because it affects how pack expansions are processed. However, instantiating Storage with Foo<int, float> makes T::types a parameter pack, while instantiating Storage with Bar makes T::types a single type.
Ok, I understand.
The "fix" for this is to do the same thing that "typename" does for "typename T::foo"... "typename" was added because the compiler can't tell whether T::foo is a a value or a type. We would need something like:
typename... T::types
to that say T::types is a type parameter pack. We'd need the same thing for value parameters packs and anything else that could be a parameter pack. It's not impossible to introduce such an extension, but doing so drastically increases the syntactic cost of variadic templates, and the addition of yet new things to worry about with dependent names makes variadic templates harder to use. For all these additions, you don't actually gain any expressive power:
tuple<Types...> values;
does basically the same thing as the requested functionality.
I was thinking of a way to make a generic binder for a given parameter in a functor implemented with variadic templates. The binder needs (I think) to recurse in the type list to find the given type (to be extracted out of the typelist) and the rest of the typelist to instantiate the return value interface type, at least. template <typename... Before, typename Item, typename... After> Bind<3, Before..., Item, After...>(const Functor<Before..., Item, After...> &, const Item &value) : public Functor<Before..., After...> {...} To evaluate this expression you need to rewrite it so you can match the first parameter pack up with a certain amount of parameters given as an int. AFAIK, the only way to do that is to take it as a whole list and to split it up by myself: template <typename... Args> Bind<3, Args...>(const Functor<Args...> &, const typeFrom<3, Args...>::type &) : public Functor<typeWithout<3, Args...>::types...> {...} I'm puzzled how I can write typeWithout::types... without a parameter pack typedef. You might be able to go from the first idea to one that checks is_equal<sizeof<Before...>, 3>::value but I'm not sure the compiler can trace that route or whether it'll be possible. Thanks for your time and patience, Peter Bindels

On Thu, 2007-09-20 at 21:19 +0200, Peter Bindels wrote:
I was thinking of a way to make a generic binder for a given parameter in a functor implemented with variadic templates. The binder needs (I think) to recurse in the type list to find the given type (to be extracted out of the typelist) and the rest of the typelist to instantiate the return value interface type, at least.
Sure, you can do that. There's a full implementation of tr1::bind in the upcoming GCC 4.3 (available from GCC's Subversion repository)s, and this requires a subset of that functionality. So it's doable, but it might not necessarily be easy. The implementation of tr1::bind is "lightly described" in N2080, the variadic templates proposal.
template <typename... Before, typename Item, typename... After> Bind<3, Before..., Item, After...>(const Functor<Before..., Item, After...> &, const Item &value) : public Functor<Before..., After...> {...}
To evaluate this expression you need to rewrite it so you can match the first parameter pack up with a certain amount of parameters given as an int. AFAIK, the only way to do that is to take it as a whole list and to split it up by myself:
Right, the above matching doesn't work.
template <typename... Args> Bind<3, Args...>(const Functor<Args...> &, const typeFrom<3, Args...>::type &) : public Functor<typeWithout<3, Args...>::types...> {...}
I'm puzzled how I can write typeWithout::types... without a parameter pack typedef. You might be able to go from the first idea to one that checks is_equal<sizeof<Before...>, 3>::value but I'm not sure the compiler can trace that route or whether it'll be possible.
You can do it with a metafunction that returns the equivalent of Functor<typeWithout<3, Args...>::types...> It will be a recursive metafunction that pulls apart "Args", skipping the third argument, and puts all of the other arguments into the Functor. An example: template<typename F, int N, typename... Args> struct functor_without_Nth_arg; template<typename... FArgs, int N, typename Arg, typename... Args> struct functor_without_Nth_arg<Functor<FArgs...>, N, Arg, Args...> : functor_without_Nth_arg<Functor<FArgs..., Arg>, N-1, Args...> { }; template<typename... FArgs, typename Arg, typename... Args> struct functor_without_Nth_arg<Functor<FArgs...>, 0, Arg, Args...> { typedef Functor<FArgs..., Args...> type; }; Now, functor_without_Nth_arg<Functor<>, 3, Args...>::type is the type you wanted. - Doug
participants (6)
-
Arkadiy Vertleyb
-
Atry
-
Doug Gregor
-
Douglas Gregor
-
Marco
-
Peter Bindels