
On 7/17/2011 3:21 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 8:43 AM, Edward Diener<eldiener@tropicsoft.com>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@gmail.com> wrote:
On Sat, Jul 16, 2011 at 7:58 PM, Jeffrey Lee Hellrung, Jr.<
jeffrey.hellrung@gmail.com> 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