Boost logo

Boost :

Subject: Re: [boost] [TTI] Review for The Type Traits Introspection library by Edward Diener **extended**
From: Edward Diener (eldiener_at_[hidden])
Date: 2011-07-17 17:49:26


On 7/17/2011 3:21 PM, Jeffrey Lee Hellrung, Jr. wrote:
> 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 :)

The type I am checking my nested type name against in has_type_name<T,U>
is U. In your proposed:

has_type_'name'<T,P> if P is a metafunction how does a U get into it? Or
are you saying that P can be passed something like 'is_same<_1,int>' ?

>
>
>> 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> >

OK.

>
>
>>>> [...]
>>>
>>> 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 :)

I really thought I explained the idea clearly and my use cases were
pretty simple. I will revisit this with better documentation.

> 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>.

The light went on finally. You want the 'type with check' idea to be
extended to any check which can be formulated in terms of a metafunction
rather than just a check that a typedef is the same as some other type.

That is a fruitful idea but I really have to think about that in terms
of the syntax I have already developed. I am not saying it can not fit
in to my current design, even most successfully.

> It seems TTI only directly supports
> introspection of nested types (and not any other inner elements), correct?

Yes. What other types of inner elements can be introspected other than
other types themselves ? Or to put it clearer, the library is about
introspecting the inner elements of a type.

>
> 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.

Yes, I now understand what you are suggesting with the second template
parameter to has_type_xxx. I need to think about this before I answer it
fully but I do not want to let it impede what already currently exists
as part of the library and the review so my thoughts may take some time
once the review ends. But I will respond eventually. I admit I am not in
love with lambda syntax as compared to the nullary type syntax and all
those nested ::type and final ::value but certainly drilling down
through 'types' in a hierarchy in the order in which they are viewed is
an advantage.

Eddie


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