Boost logo

Boost :

Subject: Re: [boost] [TTI] Review for The Type Traits Introspection library by Edward Diener **extended**
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2011-07-17 15:21:19


On Sun, Jul 17, 2011 at 8:43 AM, Edward Diener <eldiener_at_[hidden]>wrote:

> On 7/17/2011 1:25 AM, Jeffrey Lee Hellrung, Jr. wrote:
>
>> On Sat, Jul 16, 2011 at 9:56 PM, Jeffrey Lee Hellrung, Jr.<
>> jeffrey.hellrung_at_[hidden]> wrote:
>>
>> On Sat, Jul 16, 2011 at 7:58 PM, Jeffrey Lee Hellrung, Jr.<
>>> jeffrey.hellrung_at_[hidden]> wrote:
>>> [...]
>>>
>>> * I don't see a compelling use case for "has type with check", where
>>>> "check" checks if the type is the same as some given type. I would
>>>> think it
>>>> would be more useful for "check" to evaluate some Boost.MPL metafunction
>>>> (and the old behavior is arguably clearer now: boost::is_same< U,
>>>> boost::mpl::_1>), and then you can roll this functionality into the "no
>>>> check" TTI metafunction by defaulting the MPL metafunction template
>>>> parameter to boost::mpl::always< boost::true_type> (or similar).
>>>> Indeed, I
>>>> *think* this would remove the need for BOOST_TTI_MEMBER_TYPE for the use
>>>> case you present (see below).
>>>>
>>>> [...]
>>>
>>> I wanted to elaborate on this idea to see if, indeed, I can solve the
>>> same
>>> problems outlined in the sections "Nested Types" and "Using the Nullary
>>> Type
>>> Metafunctions" with little additional syntactic burden.
>>>
>>> Concretely, I'm proposing that
>>>
>>> BOOST_TTI_HAS_TYPE( name )
>>>
>>> generates a metafunction has_type_'name' such that
>>>
>>> has_type_'name'<T>::value == true iff T has a nested 'name' type
>>> has_type_'name'<T,P>::value == true iff T has a nested 'name' type *and*
>>> boost::mpl::apply1< P, T::'name'>::type::value == true
>>>
>>
> So you want your version to take as second template parameter another
> metafunction, whose template parameter is the nested type, to do the check
> rather than just specifying the 'type' as the second template parameter ?

Yes.

> I realize that this provides more flexibility

Yes.

> but at the cost of increasing the complexity of a simple check.

True, but let me put a more positive spin on it and say that you're just
being more explicit.

> It also does not readily provide the type you are checking against.
>

You mean T::'name'? Can you elaborate on what you're contrasting against?
It also does not stop global warming :)

> I like your idea as an alternative but I do not want to remove the simpler
> solution.
>

I wonder how common of a use case it is, though, compared to just checking
for existence or querying more general properties of the nested type.
Indeed, this latter use case is exactly where the "Nested Types" and "Using
the Nullary Type Metafunctions" use cases fit in. The additional syntactic
burden is, in my opinion, minimal:

has_type_xxx< T, U >

vs

has_type_xxx< T, is_same< _1, U > >

>>> [...]
>>
>> I'll investigate the use case(s) given in the section "Using the Nullary
>>> Type Metafunctions" in a separate post.
>>>
>>>
>> And here I am again. This time, our class structure looks like
>> (reformatted
>> with comments removed):
>>
>> struct T
>> {
>> struct BType
>> {
>> template< class, class, int, class template< class> class,
>> class
>> long>
>> struct ManyParameters
>> { };
>>
>> struct CType
>> {
>> template< class>
>> struct AMemberTemplate
>> { };
>>
>> struct DType
>> {
>> typedef double ADoubleType;
>>
>> template< class, class, int, short, class, template<
>> class,
>> int> class, class>
>> struct MoreParameters
>> { };
>>
>> int IntFunction(short) const
>> { return 0; }
>>
>> static short DSMember;
>>
>> static int SIntFunction(long, double)
>> { return 2; }
>> };
>> };
>> };
>>
>> BType IntBT;
>> };
>>
>> We assume the following declarations:
>>
>> BOOST_TTI_HAS_TYPE( BType )
>> BOOST_TTI_HAS_TYPE( CType )
>> BOOST_TTI_HAS_TYPE( DType )
>>
>> using boost::mpl::_1;
>> using boost::mpl::lambda;
>>
>> This would be how one would express the first two queries in the section
>> "Using the Nullary Type Metafunctions" via my above proposed change to
>> BOOST_TTI_HAS_TYPE.
>>
>> (a) Does T have a nested type called 'DType' within 'BType::CType'?
>>
>> has_type_BType<
>> T,
>> has_type_CType<
>> _1,
>> typename lambda< has_type_DType< _1> >::type
>> >
>>
>>> ::value
>>>
>>
>> (b) Does T have a nested typedef called 'ADoubleType' within
>> 'BType::CType::DType' whose type is a 'double'?
>>
>> BOOST_TTI_HAS_TYPE( ADoubleType )
>> using boost::is_same;
>>
>> has_type_BType<
>> T,
>> has_type_CType<
>> _1,
>> typename lambda< has_type_DType<
>> _1,
>> typename lambda< has_type_ADoubleType<
>> _1,
>> typename lambda< is_same< _1, double> >::type
>> > >::type
>> > >::type
>> >
>>
>
> I admit I am confused about what is going on above.
>

Well you can probably imagine what I thought when reading the "Nested Types"
and "Using the Nullary Type Metafunctions" on my first reading :) Using a
relatively complicated and ostensibly contrived example as your motivating
use case for a particular component is not going to be very convincing in
general, I think. I will use a simpler example to demonstrate my
proposition. I'll leave it as an exercise to the reader what things would
look like using BOOST_TTI_MEMBER_TYPE and/or the nullary type metafunctions.

The nullary type metafunctions are not limited to BOOST_TTI_HAS_TYPE usage

I'm not sure where this comment fits into the discussion :/ I guess you're
only seeing has_type_xxx queries above, but the "terminal" query can easily
be formulated in terms of any metafunction, including the TTI
metafunctions. E.g., just replace is_same< _1, double > above with
has_member_function_xyz< _1 >. It seems TTI only directly supports
introspection of nested types (and not any other inner elements), correct?

and I do not understand what your code above has to do with your the version
> of BOOST_TTI_HAS_TYPE you suggested.
>

The second parameter to each instantiation of has_type_xxx is a Boost.MPL
lambda expression. This gets around having to refer to T::'name' directly
when you want to make queries on T::'name' but aren't sure yet whether
T::'name' exists.

Somehow there has been a disconnect, quite possibly on my end.
>

 Agreed (that there is a disconnect), and probably because the example is
overly complex. Let's start with a simple example. We'll use the following
struct model:

struct T
{
    struct A
    {
        struct B
        {
            struct C
            {
                int x;
            };
        };
    };
};

Question: Does the type T::A::B exist?

Answer:
BOOST_TTI_HAS_TYPE( A )
BOOST_TTI_HAS_TYPE( B )
using boost::mpl::_1;
has_type_A< T, has_type_B< _1 > >::value

Just so we're on the same page, let's evaluate this query step-by-step.
First, T does indeed have a nested type A, so, as per my proposed
modification to the metafunctions generated by BOOST_TTI_HAS_TYPE, we
evaluate the Boost.MPL lambda expression passed to has_type_A (which is the
Boost.MPL placeholder expression has_type_B< _1 >) with argument T::A. This
gives has_type_B< T::A >, which evaluates to true, and hence the entire
metaexpression above evaluates to true. Follow?

Question: Does the type T::A::B::C exist?

Answer:
BOOST_TTI_HAS_TYPE( C )
using boost::mpl::lambda;
has_type_A<
    T,
    has_type_B<
        _1,
        typename lambda< has_type_C< _1 > >::type
>
>::value

The boost::mpl::lambda construct is necessary to "hide" the _1 in
has_type_C< _1 > so it doesn't get substituted for T::A when evaluating
has_type_A< _1, ... >. In other words, to contrast, suppose we had done

has_type_A< T, has_type_B< _1, has_type_C< _1 > > >

and let's go through the evaluation step-by-step. First, T indeed has a
nested type A, so we evaluate the Boost.MPL lambda expression passed to
has_type_A (has_type_B< _1, has_type_C< _1 > >) with argument T::A, giving

has_type_B< T::A, has_type_C< T::A > >

T::A has a nested type B, so the evaluation continues by evaluating
has_type_C< T::A > with argument T::A::B, but this results in compile-time
error, as has_type_C< T::A > is a nullary lambda expression. On the other
hand, using boost::mpl::lambda as in the original presentation does what we
want to do, as it turns a Boost.MPL placeholder expression into a
corresponding Boost.MPL metafunction class, so it will eventually evaluate
has_type_C< _1 > with argument T::A::B rather than argument T::A.

Question: Does the type T::A::B::C exist and have a member data x of type
int?

Answer:
BOOST_TTI_HAS_MEMBER_DATA( x )
has_type_A<
    T,
    has_type_B<
        _1,
        typename lambda< has_type_C<
            _1,
            typename lambda< has_member_data_x< _1, int > >::type
> >::type
>
>::value

I hope these simpler examples demonstrate the utility and power of this
proposition.

- Jeff


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