Boost logo

Boost :

Subject: Re: [boost] enable_if and non-template member functions?
From: Edward Diener (eldiener_at_[hidden])
Date: 2011-08-08 14:37:18


On 8/8/2011 1:44 PM, Matt Calabrese wrote:
> On Mon, Aug 8, 2011 at 8:55 AM, Jeremiah Willcock<jewillco_at_[hidden]>wrote:
>
>> I think the macro would be useful, but looking at the implementation, I'm
>> not sure it works with types like you claim. Also, what is the code on
>> lines 45-46 doing? I don't see how that would ever be used by the current
>> definitions of your macros.
>>
>
> It does, try it out. I'll comment the trick a little better or remove it if
> people have a problem with it for some reason (it implies a couple of
> template instantiations so that's a legitimate criticism, though I don't
> think use of the macro would be common enough for it to have a negligible
> impact on compile-times). The idea is that I want the single macro to be
> usable with values or types (mpl integral constants), so I need to be able
> to have the macro expand to a bit of C++ that implicitly converts the bool
> constant to an mpl integral constant and leaves the mpl constant as is, or
> the other way around. The trick I came up with to accomplish that is this:
>
> //////////
> // Make two function template overloads, here both called "foo":
>
> // One that takes a bool constant
> // The return type is the mpl integral constant equivalent of the bool
> template< bool Value> mpl::bool_< Value> foo();
>
> // And one that takes an mpl integral constant
> // The return type is just the argument being passed
> template< class Type> Type foo();
>
> // Now if we do:
> // decltype( foo< something_goes_here>() )
> // We will always get an mpl integral constant representing the argument
> // This works if something_goes_here is a bool constant or if it's an mpl
> constant
>
> // For example:
> // Yields mpl::bool_< true>
> typedef decltype( foo< true>() ) condition1;
>
> // Yields boost::is_void< void>
> typedef decltype( foo< boost::is_void< void> >() ) condition2;
> //////////
>
> Understand? So by using function template overloads and decltype you can
> convert the bool to an mpl integral constant without the user having to be
> aware. If an mpl integral constant is passed, then it is left alone.
>
> The actual trick in the code is slightly more complex and that is because I
> also handle the potentially common case of a bool condition with a greater
> than sign in it such as:
> sizeof( something )> sizeof( void* )
>
> This issue here is that the> wouldn't actually be a greater than operator
> at all after macro expansion, it would instead end the function template
> argument list that appears inside the decltype. A user who doesn't realize
> this would get a weird error, which could ultimately be resolved by just
> wrapping their expression in parentheses, but that isn't the easiest thing
> to notice for most programmers. To make it so that a user doesn't have to
> know to do that, I just wrap the argument in parentheses from the inside of
> the macro. But now, if I wrap all macro arguments internally in parentheses,
> I have the problem that a parenthesized type is not valid on its own! To get
> around that, what I do is I append "bool" in front of the parentheses. If
> you don't immediately see what that get's you, check it out:
>
> //////////
> // In the case of a value, we get a compile-time cast to bool. No problem.
> bool( sizeof( something )> sizeof( void* ) )
>
> // In the case of a type we get a function type that
> // returns bool
> // takes our condition as a parameter type
> bool( is_void< void> )
> //////////
>
> Now that I have a bool or a function type with the condition in the first
> parameter, I need to rewrite the original overloads:
>
> //////////
> // This overload stays the same
> // The return type is the mpl integral constant equivalent of the bool
> template< bool Value> mpl::bool_< Value> foo();
>
> // This overload is updated to pull out the parameter type instead of the
> type directly
> // The return type is the parameter type of the function
> template< class Type> typename function_traits< Type>::arg1_type foo();
> //////////
>
> Just to be complete, here's what the decltype part of the macro looks like
> with everything update after macro expansion (it obviously expands to more
> than just this, though here is the interesting part -- the function is also
> not called "foo" in the actual implementation, it's called
> "enable_if_condition_getter"):
>
> //////////
> // BOOST_ENABLE_IF( true )
> // Internally yields mpl::bool_< true> from the decltype trick
> // decltype( foo< bool(true)>() )
> //
> // BOOST_ENABLE_IF( boost::is_void< void> )
> // Internally yields boost::is_void< void> from the decltype trick
> // decltype( foo< bool(boost::is_void< void>)>() )
> //////////
>
> So now there can exist a single macro that works with mpl integral constants
> or bools, even if they have a top-level>, without the user of the macro
> having to know anything about the implementation. Anyway, if people really
> hate this, it can be removed and replaced by BOOST_ENABLE_IF and
> BOOST_ENABLE_IF_C which each have their own separate implementation -- one
> for types and one for bools. I just prefer to provide the simplest interface
> possible with the least subtleties for the user, even if it complicates the
> code a bit behind the scenes. If the template instantiations are perceived
> to be a problem, then two macros instead of one is a reasonable option.

You are absolutely right. It is important that the end user has the
easiest interface to do what is necessary whenever possible. The
implementation can always be designed well, and have enough comments in
the code, that another developer can understand it rather than being
dumbed down in any way because it may be difficult to understand.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk