I'm new to the Boost.MPL library, and have some "beginners-problems"
Look at this sample:
template < typename F >
struct A {
typedef boost::function_types::parameter_types<F> P;
typedef typename boost::function_types::result_type<F>::type R;
typedef typename boost::mpl::if_< boost::is_same< R, void >,
boost::function< void ( void ) > ,
boost::function< void ( R ) > >::type TTT;
A() { }
};
int main(int argc, const char *argv[]) {
A<int(int, float)> ok; // working
A<void(int, float)> compile_error; // non-working
return 0;
}
When compiling I get:
xxx.cxx: In instantiation of ‘A<void(int, float)>’:
xxx.cxx:108:25: instantiated from here
xxx.cxx:100:77: error: invalid parameter type
‘boost::mpl::aux::wrapped_type<boost::mpl::aux::type_wrapper<void>
>::type’
xxx.cxx:100:77: error: in declaration ‘A<F>::TTT’
What is the problem here, and how can I solve it?
To my understanding, only the selected part of mpl::if_
should be evaluated by the compiler....
First of all, to explain the error, it should be noted that using a typedef to void
in a parameter list is an error. These two GCC bug reports (32058 and 9278) describe the problem, an point out that this is requirement from the standard.
So basically, according to §8.3.5/2 of the standard, this is legal:
void foo(void);
while this is not:
typedef void type;
void foo(type);
This explains why you needed the if_
in the first place. Now to explain why you still have the error, you need to understand that lazy evaluation in MPL only apply to metafunctions: as long as you don't access the type
inside a metafunction, it is not evaluated. Here, the if_
's arguments are not evaluated (they could not since they are not metafunctions), but that does not mean that they are not instantiated.
To overcome this issue, you could embed the function
instantiations into metafunctions that could be lazily evaluated:
template < typename R, typename P >
struct get_function
{
typedef boost::function< R (P) > type;
};
template < typename F >
struct A {
typedef boost::function_types::parameter_types<F> P;
typedef typename boost::function_types::result_type<F>::type R;
typedef typename
boost::mpl::if_<
boost::is_same< R, void >,
boost::mpl::identity< boost::function< void (void) > > ,
get_function< void, R >
>::type::type TTT;
A() { }
};
This way, the erroneous void (typedef_to_void)
never appears.
A better solution would even be to specialize the get_function
metafunction for the void
case:
template < typename R, typename P >
struct get_function
{
typedef boost::function< R (P) > type;
};
template < typename R >
struct get_function< R, void >
{
typedef boost::function< R (void) > type;
};
template < typename F >
struct A {
typedef boost::function_types::parameter_types<F> P;
typedef typename boost::function_types::result_type<F>::type R;
typedef typename get_function< void, R >::type TTT;
A() { }
};
No more if_
needed!