[TTI] Review for The Type Traits Introspection library by Edward Diener **extended**

The Type Traits Introspection library by Edward Diener started friday 1st have been *extended* to july 17. Comments and reviews are welcome. =========== What is it? =========== The TTI library, which is an abbreviation for the 'Type Traits Introspection' library, allows a programmer to introspect at compile time the inner elements of a C++ type. The introspection process depends on specifying the name of the inner element by different macros for different types of elements, and then using a generated metafunction to determine whether that element exists within the enclosing type. The inner elements which can be introspected are type, class template, member data, member function, static member data, and static member function. The TTI library is based on the type_traits_ext portion of the Concept Traits Library, with improvements and additions, and also reproduces functionality ( without changing existing code ), for the purposes of completeness, from Boost.MPL regarding introspection of types and templates. The purpose of the library is to provide a consistent set of interfaces for doing compile-time introspection of a type, which other template metaprogrammers can use in their code. If you are at all interested in compile-time introspection of types, please take a look at the functionality of this library. =================== Getting the library =================== The library is available at http://svn.boost.org/svn/boost/sandbox/tti/. The HTML documentation is available at http://svn.boost.org/svn/boost/sandbox/tti/libs/tti/doc/html/index.html and the PDF documentation is available at http://svn.boost.org/svn/boost/sandbox/tti/libs/tti/doc/TypeTraitsIntrospect.... =================== Writing a Review =================== The reviews and all comments should be submitted to the developers list, and the email should have "[TTI] Review" at the beginning of the subject line to make sure it's not missed. Please explicitly state in your review whether the library should be accepted. The general review checklist: - What is your evaluation of the design? - What is your evaluation of the implementation? - What is your evaluation of the documentation? - What is your evaluation of the potential usefulness of the library? - Did you try to use the library? With what compiler? Did you have any problems? - How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? - Are you knowledgeable about the problem domain? And finally, every review should answer this question: - Do you think the library should be accepted as a Boost library? Be sure to say this explicitly so that your other comments don't obscure your overall opinion.

On Thu, Jul 7, 2011 at 5:15 AM, Joel Falcou <joel.falcou@gmail.com> wrote:
The Type Traits Introspection library by Edward Diener started friday 1st have been *extended* to july 17. Comments and reviews are welcome.
[...]
Please explicitly state in your review whether the library should be accepted.
Yes.
The general review checklist:
- What is your evaluation of the design?
Fundamentally sound.
- What is your evaluation of the implementation?
I will try to look into it before the end of the review; specifically, I'd like to compare it to what I have in my own code base.
- What is your evaluation of the documentation?
Generally very good. I think it will be improved if the reference sections for each component also include examples. Also, I think the reference section for each macro is not very clear. I'd prefer something like -------- BOOST_TTI_HAS_TYPE( name ) Description Generates a Boost.MPL-compatible metafunction boost::tti::has_type_'name', with the following semantics. -------- with a table giving all relevant expressions (e.g., boost::tti::has_type_'name'<T>::value), what the parameters are (T is any type), and what the result is (true iff T has a nested 'name' type). I think it's similarly unclear from a quick glance what the types T, U, R, etc. are in Table 1.2 in the "Macro Metafunctions" section. - What is your evaluation of the potential usefulness of the library?
Indeed, very useful; I have similar such metafunction-generating macros that I have used for a while now, and I look forward to replacing them with Eddie's work.
- Did you try to use the library? With what compiler? Did you have any problems?
Not yet.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
I've been nonlinearly patching together this review for a few days now, so it might come off as a bit disjointed. I apologize in advance.
- Are you knowledgeable about the problem domain?
I think so.
And finally, every review should answer this question:
- Do you think the library should be accepted as a Boost library?
// ugliest, but most flexible with Boost.MPL - Likewise, I think BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION can subsume BOOST_TTI_HAS_COMP_STATIC_MEMBER_FUNCTION, and likewise I think the syntax has_static_member_function_xxx< T, Return ( Arg0, Arg1 ) > would be convenient. - Although this doesn't have to do with condensing macros, it would make
[...] Again, yes. Ideally, Eddie would accept 100% of my suggestions below, but I am a realistic person (...sometimes): (a) I could be wrong (very wrong!); and/or (b) some issues have multiple legitimate viewpoints. So all I ask is he consider my comments below. First, a general comment. There's a lot more in this library than I expected at first, some of which is welcome but some of which I think might be unnecessary, in that, e.g., it only provides a slight convenience, or only addresses a (in my opinion) obscure use case. You (Eddie) may argue that there's no harm in including it, as users can simply ignore it, but I disagree. I believe there is value in being judicious about what to include and not include. It simplifies the presentation of the library, making it easier to digest; and it reduces maintenance. More detailed comments follow, in roughly the order I thought of them as I read the documentation. Most of them are critical ("Dude, WTF, you should do X instead of what you're doing now"), and I apologize in advance for not mentioning the things I believe you did correctly. I just want to get straight to the point of likely the most contentious and discussable issues :) * I would suggest a name change from "TTI" to "Introspection" or something similar. I have a feeling that, looking at a source file with "#include <boost/tti/header.hpp>" would give one less of an idea of what the purpose of that header is than "#include <boost/introspection/header.hpp>", and likewise for macros, "BOOST_TTI_MACRO" vs. "BOOST_INTROSPECTION_MACRO". I don't think the length of the name should be a huge concern since I would imagine the ratio of metafunction-generating-macro use to generated-metafunction use is much less than 1, i.e., the macro will not be used frequently relative to the metafunction that's generated. * As others have noted and as I believe Edward has agreed to, generated metafunctions should be injected into the scope of the macro invocation, not in the boost::tti namespace. * Contrary to others opinions, I think Edward should keep both BOOST_TTI_MACRO and BOOST_TTI_TRAIT_MACRO macros (optionally renaming them, if desired; I think these are just following the Boost.MPL naming scheme, but perhaps we can think of names that evoke the difference a little more clearly). It imparts a de facto naming convention for these metafunctions. *But* you should include a warning about the possibility to automatically generate names with double underscores, and that this behavior is undefined. * As others have noted, the association between file names and the macros they define is confusing, so I think we should strive for a more predictable association. * I'm confused what the purpose of the *_GEN_BASE and *_GEN macros are, and suspect they are probably unnecessary. * I *think* all generated metafunctions should also expose a nested "type" typedef in addition to a nested "value" static bool in order to be fully Boost.MPL compatible, but...I'm not sure on this one. For some reason, I've always followed this practice, I think because it allows has_type_xxx<T> to be used as a nullary Boost.MPL metafunction. Can someone comment on this? * I think it would be more helpful if the examples were paired with the reference section of the corresponding macro. * 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 like Lorenzo's idea to condense some related macros into a single macro: - I think BOOST_TTI_HAS_TEMPLATE can subsume BOOST_TTI_HAS_TEMPLATE_CHECK_PARAMS and BOOST_TTI_VM_HAS_TEMPLATE_CHECK_PARAMS. - I think BOOST_TTI_HAS_MEMBER_FUNCTION can subsume BOOST_TTI_HAS_COMP_MEMBER_FUNCTION, since I think you could just dispatch on whether the first template parameter to the generated metafunction is a pointer-to-member-function or not. Another convenient syntax may be has_member_function_xxx< T, Return ( Arg0, Arg1 ) >, so that all of the following are equivalent: has_member_function_xxx< Return (T::*)( Arg0, Arg1 ) > has_member_function_xxx< T, Return ( Arg0, Arg 1 ) > // can still use lambda expressions for T, has similar format as other introspection metafunctions has_member_function_xxx< T, Return, boost::mpl::vector2< Arg0, Arg1 the library a little more consistent to allow pointer-to-member syntax for BOOST_TTI_HAS_MEMBER_DATA, e.g., has_member_data_xxx< U T::* >. - Lastly, BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION( xxx ) can be reexpressed as BOOST_TTI_HAS_MEMBER_FUNCTION( static xxx ), and likewise for BOOST_TTI_HAS_STATIC_MEMBER_DATA. * For BOOST_TTI_HAS_TEMPLATE where you supply the template signature, I'm actually fine with the old system of wrapping the signature in parentheses and replacing all commas with ")(" to make a Boost.PP Seq, and prefer it to the array syntax you've proposed since. That said, I would probably, ultimately, prefer Lorenzo's syntax (e.g., "TTI_HAS_TEMPLATE( template( class, class ) struct tmpl )"), but I guess we'll agree to disagree on what's easier to read (readability should win over writability, right?). For the syntax taking advantage of variadic macros that you've proposed during the review (e.g., "TTI_HAS_TEMPLATE( tmpl, template< class, class > )"), I worry about usability issues when you're just stringing the template signature into the __VA_ARGS__ argument. I don't use variadic macros yet, so my sense of "good practice" is very malleable at the moment, but, that being said, it seems like good practice is to use variadic macro arguments only when (a) your macro logically takes a fixed number of arguments, but where you can syntactically leave off trailing arguments that you wish to be defaulted; or (b) your macro takes a varying number of arguments, but the semantic difference between each argument is purely and strictly positional. To me, the template signature is a completely separate argument from the template name, and the syntax should reflect that, e.g., TTI_HAS_TEMPLATE( tmpl, (template< class, class >) ) or TTI_HAS_TEMPLATE( tmpl, ( class, class ) ). It also seems like this would make it easier to use TTI_HAS_TEMPLATE within other preprocessor metaprogramming constructs (e.g., if I wanted to generate an entire family of metafunctions via BOOST_PP_REPEAT or BOOST_PP_ITERATE), but perhaps you can argue otherwise. * When introspecting for nested templates and member functions, how "precise" is the result? I.e., if you're checking if T has member function xxx with signature void ( ), and T in fact has a member function xxx with signature void ( int = 0 ), is the result false? (I'm guessing yes, and this should be noted in the reference.) Similarly for nested templates with default template parameters. [The next three comments have been echoed by others.] * Please document is how the accessibility of the inner elements affects whether metafunction invocations can be compile and what their result is. * Please document in which scopes you may invoke the metafunction-generating macros. * Please provide rationale for why you support introspection some properties but not others (e.g., namespace introspection, virtual functions). I assume this is a limitation of the C++ language. * The use case you give to motivate BOOST_TTI_MEMBER_TYPE and valid_member_type seems somewhat contrived. I have never run into this situation further than nesting 1 level deep. However, assuming the validity of the use case, if you have a has_type_xxx< T, Predicate > metafunction, you can determine whether T::AType::BType::CType::FindType exists without a compiler the error in the event that one of the intermediate types doesn't exist via has_type_AType< T, has_type_BType< boost::mpl::_1, has_type_CType< boost::mpl::_1, has_type_FindType< boost::mpl::_1 > > >
Oops, okay, that won't work without some additional boost::mpl::protect's, but hopefully you get the idea. Let me know if you want me to elaborate. * Similar to above, I'm not convinced of the utility of the metafunction-class-generated macros...aren't they largely obviated by boost::mpl::quote[n]? * Lastly, and I'm not absolutely sure about this, but it seems like adding metafunction predicate parameters to the has_type_xxx generated metafunctions would obviate the need for the nullary metafunction infrastructure. I can elaborate on this further if you don't understand what I mean. * It's *awesome* that there's an index...generated by John Maddock's AutoIndex tool? I'm open to discussing the above issues beyond the review period, so, Eddie, you may respond at your convenience. - Jeff

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 Now, let's suppose the proposed type structure in the section "Nested Types", which I've slightly modified for convenience: struct T { struct A { struct B { struct C { struct X { }; }; }; }; }; Let's also suppose BOOST_TTI_HAS_TYPE( A ) BOOST_TTI_HAS_TYPE( B ) BOOST_TTI_HAS_TYPE( C ) BOOST_TTI_HAS_TYPE( X ) First, let me create a metafunction has_type_TABCX to "drill down" to T::A::B::C::X; I would equate has_TABCX to the MyFindType typedef in the "Nested Types" section. using boost::mpl::_1; using boost::mpl::always; using boost::mpl::lambda; using boost::mpl::protect; using boost::mpl::true_; template< class P = always< true_ > > struct has_type_TABCX : has_type_A< T, has_type_B< _1, typename lambda< has_type_C< _1, typename lambda< has_type_X< _1, protect< typename lambda<P>::type > > >::type > >::type > > { }; Unfortunately, I haven't tested the above, so I'm not 100% certain the above is correct...but I think it is :) I threw the extra protect in there to prevent unintended substitutions if P is a bind-but-not-placeholder expression. I'll note that de Bruijn indices [1] would make the above a little bit cleaner, but we're stuck with traditional lambda placeholders in Boost.MPL. Okay, first challenge: Does T::A::B::C::X exist? has_type_TABCX<>::value Second challenge: Does T::A::B::C::X exist and does it have a static member data called MyData of type int? BOOST_TTI_HAS_STATIC_MEMBER_DATA( MyData ) using boost::is_same; has_type_TABCX< has_static_member_data_MyData< _1, typename lambda< is_same< int, _1 > >::type >
::value
I'm satisfied enough with the above usage to believe that BOOST_TTI_MEMBER_TYPE and boost::tti::valid_member_type are not really necessary, as I don't see them offering a significant syntactical advantage. That is, assuming I got my syntax above correct :/ Eddie, what are your thoughts? I certainly might've made a mistake, and there might be cases that BOOST_TTI_MEMBER_TYPE + boost::tti::valid_member_type address but where the above strategy fails, I don't know... I'll investigate the use case(s) given in the section "Using the Nullary Type Metafunctions" in a separate post. - Jeff [1] http://en.wikipedia.org/wiki/De_Bruijn_index

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
[...]
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 think the remaining queries look very similar and I don't foresee any difficulty in constructing them. Like my previous post, some repetitive constructs can be refactored into a separate template if you find yourself making multiple queries on a deeply nested type. I also think this syntax additionally has the advantage of directly reflecting the hierarchy of types you're querying, rather than reversing it. I.e., has_type_BType above is at the outermost level of the query and has_type_ADoubleType is at the innermost level of the query; on the other hand, using BOOST_TTI_MEMBER_TYPE or the nullary metafunctions appears to put the innermost type you're querying at the outermost level and vice versa. - Jeff

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 ? I realize that this provides more flexibility but at the cost of increasing the complexity of a simple check. It also does not readily provide the type you are checking against. I like your idea as an alternative but I do not want to remove the simpler solution.
[...]
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. The nullary type metafunctions are not limited to BOOST_TTI_HAS_TYPE usage and I do not understand what your code above has to do with your the version of BOOST_TTI_HAS_TYPE you suggested. Somehow there has been a disconnect, quite possibly on my end. Eddie

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

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

On Sun, Jul 17, 2011 at 2:49 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/17/2011 3:21 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
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.
Yep.
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.
I don't think it changes any syntax other than advanced usage of has_type_xxx and eliminating the need (from what I've seen so far, anyway) of BOOST_TTI_MEMBER_TYPE, boost::tti::valid_member_type, and the nullary type metafunction framework.
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
Let's contrast the above with the solutions presently in the documentation: // Using MEMBER_TYPE BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_HAS_TYPE( B ) has_type_B< typename member_type_A<T>::type >::value // Using nullary type metafunctions BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_HAS_TYPE( B ) using boost::mpl::_1; using boost::mpl::identity; mf_has_type< has_type_B< _1 >, mf_member_type< member_type_A< _1 >, identity<T> >
::value
Did I get those right? I'm not sure :( 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
Again, let's contrast the above with the solutions presently in the documentation.
// Using MEMBER_TYPE BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_MEMBER_TYPE( B ) BOOST_TTI_HAS_TYPE( C ) has_type_C< typename member_type_B< typename member_type_A<T>::type >::type
::value
// Using nullary type metafunctions BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_MEMBER_TYPE( B ) BOOST_TTI_HAS_TYPE( C ) using boost::mpl::_1; using boost::mpl::identity; mf_has_type< has_type_C< _1 >, mf_member_type< member_type_B< _1 >, mf_member_type< member_type_A< _1 >, identity<T> > >
::value
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
Last example.
// Using MEMBER_TYPE BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_MEMBER_TYPE( B ) BOOST_TTI_MEMBER_TYPE( C ) BOOST_TTI_HAS_MEMBER_DATA( x ) has_member_data_x< typename member_type_C< typename member_type_B< typename member_type_A<T>::type >::type >::type, int
::value
// Using nullary type metafunctions BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_MEMBER_TYPE( B ) BOOST_TTI_MEMBER_TYPE( C ) BOOST_TTI_HAS_MEMBER_DATA( x ) using boost::mpl::_1; using boost::mpl::identity; mf_has_member_data< has_member_data_x< _1 >, mf_member_type< member_type_C< _1 >, mf_member_type< member_type_B< _1 >, mf_member_type< member_type_A< _1 >, identity<T> > > >
::value
I might be biased, but I see several advantages to my proposed solutions: (a) Proposed solution: write the query from the top down; present solutions: write the query from the bottom up. (b) Proposed solution: reuse existing metafunction generation framework, no need to learn additional constructs outside of boost::mpl::lambda (and then only for deeply nested queries); present solution: need to learn a number of new structures, including MEMBER_TYPE, mf_has_*, and mf_member_type. (c) Referring to the last example above, the proposed solution actually takes about as many non-whitespace characters (I counted 120) as the "MEMBER_TYPE" solution (I counted 118) and far fewer than the "nullary type metafunction" solution (I didn't bother counting, but it certainly *looks* about 50% longer). In the first example, I think the proposed solution is clearly the winner in terms of fewest characters. [If only we had de Bruijn indices in Boost.MPL...] FWIW, I had a lot more difficulty figuring out what the right queries were in terms of the present syntax than with the proposed syntax, but then I consider myself well-versed in Boost.MPL. I think it was actually confusing for me that you mixed the metafunction class syntax with the metafunction syntax in the "Using the Nullary Type Metafunctions" example :/ It took me a while to convince myself that mtfc_member_type_CType could just as easily have been replaced with member_type_CType<_>. 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.
It is a significant change to the current interface, I agree, but (of course) I believe it is a change in the right direction.
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
Well, let's be fair here. The "MEMBER_TYPE" solutions have the typename ... ::type constructs as well (I know it's not your preferred solution); the nullary type syntax has an extra mf_member_type at each level of the query hierarchy; and I just tacked on the final ::value so I was getting a boolean expression in the end. Present solutions have that, too.
but certainly drilling down through 'types' in a hierarchy in the order in which they are viewed is an advantage.
Indeed. - Jeff

On 7/17/2011 7:55 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 2:49 PM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/17/2011 3:21 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
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.
Yep.
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.
I don't think it changes any syntax other than advanced usage of has_type_xxx and eliminating the need (from what I've seen so far, anyway) of BOOST_TTI_MEMBER_TYPE, boost::tti::valid_member_type, and the nullary type metafunction framework.
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
Let's contrast the above with the solutions presently in the documentation:
// Using MEMBER_TYPE BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_HAS_TYPE( B ) has_type_B< typename member_type_A<T>::type>::value
// Using nullary type metafunctions BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_HAS_TYPE( B ) using boost::mpl::_1; using boost::mpl::identity; mf_has_type< has_type_B< _1>, mf_member_type< member_type_A< _1>, identity<T> >
::value
Did I get those right? I'm not sure :(
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
Again, let's contrast the above with the solutions presently in the documentation.
// Using MEMBER_TYPE BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_MEMBER_TYPE( B ) BOOST_TTI_HAS_TYPE( C ) has_type_C< typename member_type_B< typename member_type_A<T>::type >::type
::value
// Using nullary type metafunctions BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_MEMBER_TYPE( B ) BOOST_TTI_HAS_TYPE( C ) using boost::mpl::_1; using boost::mpl::identity; mf_has_type< has_type_C< _1>, mf_member_type< member_type_B< _1>, mf_member_type< member_type_A< _1>, identity<T> > >
::value
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
Last example.
// Using MEMBER_TYPE BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_MEMBER_TYPE( B ) BOOST_TTI_MEMBER_TYPE( C ) BOOST_TTI_HAS_MEMBER_DATA( x ) has_member_data_x< typename member_type_C< typename member_type_B< typename member_type_A<T>::type >::type >::type, int
::value
// Using nullary type metafunctions BOOST_TTI_MEMBER_TYPE( A ) BOOST_TTI_MEMBER_TYPE( B ) BOOST_TTI_MEMBER_TYPE( C ) BOOST_TTI_HAS_MEMBER_DATA( x ) using boost::mpl::_1; using boost::mpl::identity; mf_has_member_data< has_member_data_x< _1>, mf_member_type< member_type_C< _1>, mf_member_type< member_type_B< _1>, mf_member_type< member_type_A< _1>, identity<T> > > >
::value
I might be biased, but I see several advantages to my proposed solutions: (a) Proposed solution: write the query from the top down; present solutions: write the query from the bottom up.
That is a nice feature.
(b) Proposed solution: reuse existing metafunction generation framework, no need to learn additional constructs outside of boost::mpl::lambda (and then only for deeply nested queries); present solution: need to learn a number of new structures, including MEMBER_TYPE, mf_has_*, and mf_member_type.
I think you are still missing what MEMBER_TYPE is used for in other contexts. This is when you need to supply a type whose type depends on some unknown T and is nested to some depth, and the type may or may not exist. In this case using complicated logic checking on whether or not HAS_TYPE exists becomes difficult. Now I grant you I may be able to build a better MEMBER_TYPE based on your idea above, and I will look into it. Also despite your feeling that the mullary type metafunctions should go away they still, in my mind, are valuable as an alternate syntax to the macro metafunctions even with your has_type extension idea.
(c) Referring to the last example above, the proposed solution actually takes about as many non-whitespace characters (I counted 120) as the "MEMBER_TYPE" solution (I counted 118) and far fewer than the "nullary type metafunction" solution (I didn't bother counting, but it certainly *looks* about 50% longer). In the first example, I think the proposed solution is clearly the winner in terms of fewest characters. [If only we had de Bruijn indices in Boost.MPL...]
FWIW, I had a lot more difficulty figuring out what the right queries were in terms of the present syntax than with the proposed syntax, but then I consider myself well-versed in Boost.MPL. I think it was actually confusing for me that you mixed the metafunction class syntax with the metafunction syntax in the "Using the Nullary Type Metafunctions" example :/
I perhaps should not have done that. I was not trying to show off but just to show that either could be used interchangeably with the same result. I should have kept it simple and stayed with the placeholder syntax while mentioning the alternative metafunction class syntax.
It took me a while to convince myself that mtfc_member_type_CType could just as easily have been replaced with member_type_CType<_>.
Again my fault of not just explaining my examples better.
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.
It is a significant change to the current interface, I agree, but (of course) I believe it is a change in the right direction.
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
Well, let's be fair here. The "MEMBER_TYPE" solutions have the typename ... ::type constructs as well (I know it's not your preferred solution); the nullary type syntax has an extra mf_member_type at each level of the query hierarchy; and I just tacked on the final ::value so I was getting a boolean expression in the end. Present solutions have that, too.
but certainly drilling down through 'types' in a hierarchy in the order in which they are viewed is an advantage.
I still feel that if I add your idea it will be added as an addition and not as a complete replacement of anything else. My reasoning is that not everyone is as knoweldgable about MPL and as comfortable with lambda expressions as you are. Eddie

On Sun, Jul 17, 2011 at 6:23 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/17/2011 7:55 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
I think you are still missing what MEMBER_TYPE is used for in other contexts. This is when you need to supply a type whose type depends on some unknown T and is nested to some depth, and the type may or may not exist. In this case using complicated logic checking on whether or not HAS_TYPE exists becomes difficult. Now I grant you I may be able to build a better MEMBER_TYPE based on your idea above, and I will look into it.
Hmmm..very true, I guess something like that could and will come in handy. The use case I'm thinking of is typedef typename boost::mpl::eval_if< has_type_value_type<T>, get_type_value_type<T>, boost::mpl::identity< some_default_value_type >
::type value_type;
I think one of the things I don't like about the current setup is that there are separate HAS_TYPE and MEMBER_TYPE macros, and hence separate metafunctions, and I'd imagine implementation-wise they largely do the same thing. Indeed, MEMBER_TYPE is probably implemented in terms of HAS_TYPE, and likewise for their corresponding metafunctions, right? If I want to do both "has" queries and "get" queries, then I have to invoke both HAS_TYPE and MEMBER_TYPE macros. I guess I *could* use valid_member_type< typename member_type_xxx<T>::type > to implement has_type_xxx<T>, is that correct? I also don't think the *name* MEMBER_TYPE / member_type_xxx really conveys well what this macro / metafunction combination does; I don't have any alternatives at the moment. This might not be a good idea, but maybe it could lead a better alternative. What if we added a nested template get to has_type_xxx that was a unary metafunction? The semantics could be has_type_xxx<T>::template get<>::type => equivalent to T::xxx (and errors if a nested type with no such name exists) has_type_xxx<T>::template get<U>::type => if T has a nested type named xxx, evaluates to xxx, else evaluates to U Also despite your feeling that the mullary type metafunctions should go away
they still, in my mind, are valuable as an alternate syntax to the macro metafunctions even with your has_type extension idea.
[...]
I still feel that if I add your idea it will be added as an addition and not as a complete replacement of anything else. My reasoning is that not everyone is as knoweldgable about MPL and as comfortable with lambda expressions as you are.
That would make 3 or 4 ways (depending on how you count it) to do the same thing (deeply nested type introspection): 1) the solution described in "Nested Types" using (only) MEMBER_TYPE / member_type_xxx; 2) the solution described in "Using Nullary Type Metafunctions" using TTI metafunctions; 3) the solution described in "Using Nullary Type Metafunctions" using TTI metafunction classes; and 4) the proposed new solution using Boost.MPL lambda expression predicates within the has_type_xxx queries. I think, as a general rule, if you have 3 or 4 ways to do the same thing, you're asking for confusion. I see potential value in being able to extract the nested types "softly" *something like* what MEMBER_TYPE does now, but I do find solutions 1, 2, and 3 at least as complicated as 4 for doing purely introspection queries on deeply nested types. I'm afraid neither of us, however, are a good judge of the learning effort any of the above 4 techniques require :/ I wonder if others can share their thoughts on this...? Regarding your desire to keep the nullary type metafunctions...shouldn't one be able to turn *any* metafunction into a "nullary type metafunction" in a generic fashion, similar to how boost::mpl::quote[n] can turn any metafunction into a metafunction class? I would think providing such a generic component would be more useful (and, ultimately, less work, less documentation, and less maintenance) than providing a specialized TTI nullary type metafunction for each TTI metafunction category. What do you think about adding such a component to Boost.MPL? Regarding others' knowledge of Boost.MPL, I think we should keep in mind that we're squabbling (well...I'm squabbling) about an advanced (in my opinion) use of TTI, and the amount of Boost.MPL knowledge one needs to effectively use TTI in this way is, I think, relatively small (placeholder expressions + boost::mpl::lambda). I think the learning curve would be relatively shallow if given a couple of examples. And you typically only need boost::mpl::lambda if you're introspecting 2 levels deep. - Jeff

On 7/17/2011 11:35 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 6:23 PM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/17/2011 7:55 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
I think you are still missing what MEMBER_TYPE is used for in other contexts. This is when you need to supply a type whose type depends on some unknown T and is nested to some depth, and the type may or may not exist. In this case using complicated logic checking on whether or not HAS_TYPE exists becomes difficult. Now I grant you I may be able to build a better MEMBER_TYPE based on your idea above, and I will look into it.
Hmmm..very true, I guess something like that could and will come in handy. The use case I'm thinking of is
typedef typename boost::mpl::eval_if< has_type_value_type<T>, get_type_value_type<T>, boost::mpl::identity< some_default_value_type>
::type value_type;
I think one of the things I don't like about the current setup is that there are separate HAS_TYPE and MEMBER_TYPE macros, and hence separate metafunctions, and I'd imagine implementation-wise they largely do the same thing.
MPL is very clear about not globbing more than one thing as a result of a metafunction. I agree with this.
Indeed, MEMBER_TYPE is probably implemented in terms of HAS_TYPE, and likewise for their corresponding metafunctions, right?
Yes.
If I want to do both "has" queries and "get" queries, then I have to invoke both HAS_TYPE and MEMBER_TYPE macros. I guess I *could* use valid_member_type< typename member_type_xxx<T>::type> to implement has_type_xxx<T>, is that correct?
Yes.
I also don't think the *name* MEMBER_TYPE / member_type_xxx really conveys well what this macro / metafunction combination does; I don't have any alternatives at the moment.
This might not be a good idea, but maybe it could lead a better alternative. What if we added a nested template get to has_type_xxx that was a unary metafunction? The semantics could be
has_type_xxx<T>::template get<>::type => equivalent to T::xxx (and errors if a nested type with no such name exists) has_type_xxx<T>::template get<U>::type => if T has a nested type named xxx, evaluates to xxx, else evaluates to U
I understand this but I am not enamoured of your syntax. Let me think about it.
Also despite your feeling that the mullary type metafunctions should go away
they still, in my mind, are valuable as an alternate syntax to the macro metafunctions even with your has_type extension idea.
[...]
I still feel that if I add your idea it will be added as an addition and not as a complete replacement of anything else. My reasoning is that not everyone is as knoweldgable about MPL and as comfortable with lambda expressions as you are.
That would make 3 or 4 ways (depending on how you count it) to do the same thing (deeply nested type introspection):
1) the solution described in "Nested Types" using (only) MEMBER_TYPE / member_type_xxx; 2) the solution described in "Using Nullary Type Metafunctions" using TTI metafunctions; 3) the solution described in "Using Nullary Type Metafunctions" using TTI metafunction classes; and 4) the proposed new solution using Boost.MPL lambda expression predicates within the has_type_xxx queries.
I think, as a general rule, if you have 3 or 4 ways to do the same thing, you're asking for confusion.
We have a different philosophy about this. First of all there are really three solutions, with your suggestion added to the two I already had. Your 2) and 3) above are hardly 'different". Secondly MEMBER_TYPE can be used in any scenario where a nested type has to be passed in a TTI metafunction where it may not exist. This includes as a type in member function and member data expressions in TTI. I do hear you when you say you want HAS_TYPE to also serve the purpose which MEMBER_TYPE has now so as not to have to duplicate HAS_TYPE and MEMBER_TYPE functionality. Thirdly I do not really see where the "one way is the only right way" is best here. Finally, My previous two techniques, macro metafunctions and nullary type metafunctions, are generalized ways to use TTI where the latter is merely a syntactical improvement over the former for people like me who don't like to have to grab the nested "::type" in metaprogramming constructs all the time. Obviously the macor metafunctions are not going to go away as they are the core of TTI. If nobody wants to use the nullary metafunctions except me that is fine but I see no harm in keeping them in the library
I see potential value in being able to extract the nested types "softly" *something like* what MEMBER_TYPE does now, but I do find solutions 1, 2, and 3 at least as complicated as 4 for doing purely introspection queries on deeply nested types. I'm afraid neither of us, however, are a good judge of the learning effort any of the above 4 techniques require :/ I wonder if others can share their thoughts on this...?
Regarding your desire to keep the nullary type metafunctions...shouldn't one be able to turn *any* metafunction into a "nullary type metafunction" in a generic fashion, similar to how boost::mpl::quote[n] can turn any metafunction into a metafunction class? I would think providing such a generic component would be more useful (and, ultimately, less work, less documentation, and less maintenance) than providing a specialized TTI nullary type metafunction for each TTI metafunction category. What do you think about adding such a component to Boost.MPL?
An interesting idea.
Regarding others' knowledge of Boost.MPL, I think we should keep in mind that we're squabbling (well...I'm squabbling) about an advanced (in my opinion) use of TTI, and the amount of Boost.MPL knowledge one needs to effectively use TTI in this way is, I think, relatively small (placeholder expressions + boost::mpl::lambda). I think the learning curve would be relatively shallow if given a couple of examples. And you typically only need boost::mpl::lambda if you're introspecting 2 levels deep.
I agree. I am not saying your improvement to has_type is no valuable. I am simply saying that the rest of TTI is still necessary in my opinion for other reasons. But I will think about HAS_TYPE and MEMBER_TYPE further to see if I can not combine them wih your extended idea about HAS_TYPE. Eddie

On Mon, Jul 18, 2011 at 5:26 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/17/2011 11:35 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 6:23 PM, Edward Diener<eldiener@tropicsoft.com**
wrote:
On 7/17/2011 7:55 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
I think you are still missing what MEMBER_TYPE is used for in other
contexts. This is when you need to supply a type whose type depends on some unknown T and is nested to some depth, and the type may or may not exist. In this case using complicated logic checking on whether or not HAS_TYPE exists becomes difficult. Now I grant you I may be able to build a better MEMBER_TYPE based on your idea above, and I will look into it.
Hmmm..very true, I guess something like that could and will come in handy. The use case I'm thinking of is
typedef typename boost::mpl::eval_if< has_type_value_type<T>, get_type_value_type<T>, boost::mpl::identity< some_default_value_type>
::type value_type;
I think one of the things I don't like about the current setup is that there are separate HAS_TYPE and MEMBER_TYPE macros, and hence separate metafunctions, and I'd imagine implementation-wise they largely do the same thing.
MPL is very clear about not globbing more than one thing as a result of a metafunction. I agree with this.
:: nod ::
I also don't think the *name* MEMBER_TYPE / member_type_xxx really conveys
well what this macro / metafunction combination does; I don't have any alternatives at the moment.
Actually, I'm not finding it so bad anymore. But you might do well putting it into its own separate section in the documentation.
This might not be a good idea, but maybe it could lead a better
alternative. What if we added a nested template get to has_type_xxx that was a unary metafunction? The semantics could be
has_type_xxx<T>::template get<>::type => equivalent to T::xxx (and errors if a nested type with no such name exists) has_type_xxx<T>::template get<U>::type => if T has a nested type named xxx, evaluates to xxx, else evaluates to U
I understand this but I am not enamoured of your syntax. Let me think about it.
Yeah I'm not totally on board either. Another idea is to have a special, additional macro generate both metafunctions. Also despite your feeling that the mullary type metafunctions should go
away
they still, in my mind, are valuable as an alternate syntax to the macro metafunctions even with your has_type extension idea.
[...]
I still feel that if I add your idea it will be added as an addition and
not as a complete replacement of anything else. My reasoning is that not everyone is as knoweldgable about MPL and as comfortable with lambda expressions as you are.
That would make 3 or 4 ways (depending on how you count it) to do the same thing (deeply nested type introspection):
1) the solution described in "Nested Types" using (only) MEMBER_TYPE / member_type_xxx; 2) the solution described in "Using Nullary Type Metafunctions" using TTI metafunctions; 3) the solution described in "Using Nullary Type Metafunctions" using TTI metafunction classes; and 4) the proposed new solution using Boost.MPL lambda expression predicates within the has_type_xxx queries.
I think, as a general rule, if you have 3 or 4 ways to do the same thing, you're asking for confusion.
We have a different philosophy about this.
First of all there are really three solutions, with your suggestion added to the two I already had. Your 2) and 3) above are hardly 'different".
Like I said, it depends on how you count. They're as different as the the metafunctions and metafunction classes are different. It's moot anyway if you're dropping the metafunction classes in favor of boost::mpl::quote[n]. Secondly MEMBER_TYPE can be used in any scenario where a nested type has to
be passed in a TTI metafunction where it may not exist. This includes as a type in member function and member data expressions in TTI. I do hear you when you say you want HAS_TYPE to also serve the purpose which MEMBER_TYPE has now so as not to have to duplicate HAS_TYPE and MEMBER_TYPE functionality.
Yeah but I'm starting to believe this duplication is inevitable.
Thirdly I do not really see where the "one way is the only right way" is best here.
I'm not saying, in general, that if there are 3 (or 4) ways to do the same thing, that one necessarily has to be the best and right way. Indeed, solution (1) is naturally possible with anything remotely like member_type_xxx, and solution (4) is naturally possible assuming has_type_xxx accepts Boost.MPL lambda expressions. Really the only argument I can make for (4) over (1) is it arguably has a more natural nesting order and it doesn't require any additional constructs beyond has_ queries. As for (2) (say)...I think I would be more accepting of it there was just *one* generic component that wrapped metafunctions into nullary type metafunctions, rather than 10 as there are currently, and framed the use of nullary type metafunctions (i.e., fully lazy MPL) in a more general context. By itself, I don't see the value it provides. Specifically, I'm missing... Finally, My previous two techniques, macro metafunctions and nullary type
metafunctions, are generalized ways to use TTI where the latter is merely a syntactical improvement over the former for people like me who don't like to have to grab the nested "::type" in metaprogramming constructs all the time.
...this :( You're trading "typename ... ::type" (and these are only necessary in the deeper levels of the query for solution (4) above, if you want) for mf_member_type and mf_has_whatever. See my previous email comparing the syntaxes of the various approaches (which, I admit, may have been unfair as I'm not sure if I got the other solutions right...I *think* I did, though). This trade does not seem to be an improvement, at least not syntactically. If you can convince me otherwise, I think, at a minimum, it will help the documentation. Side-by-side comparisons would certainly help. Speaking of which, no matter how many techniques you present to perform deeply nested type introspection (or any other task), it's important to convey to the user the pros and cons of each technique, so that they can make an informed decision. Just to reiterate, I don't feel like you've made your case (after reading the documentation) that the nullary type metafunctions are a syntactic improvement, which is their raison d'etre according to the documentation. Obviously the macor metafunctions are not going to go away as they are the
core of TTI. If nobody wants to use the nullary metafunctions except me that is fine but I see no harm in keeping them in the library
I think (hypothetically) if you're the only one who wants to use a component of your library (or of any library), generally either (a) whatever documentation on the component is not doing a good job conveying its purpose, and/or only you know how to really use it; (b) you should be using something else (i.e., everyone else has it right); or (c) your needs are (highly?) specialized. In cases (b) and (c), I think I have to seriously question whether the component is appropriate for Boost. In case (a), it's at least not ready for Boost. [...] - Jeff

On 7/18/2011 10:27 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jul 18, 2011 at 5:26 PM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/17/2011 11:35 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 6:23 PM, Edward Diener<eldiener@tropicsoft.com**
wrote:
On 7/17/2011 7:55 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
I think you are still missing what MEMBER_TYPE is used for in other
contexts. This is when you need to supply a type whose type depends on some unknown T and is nested to some depth, and the type may or may not exist. In this case using complicated logic checking on whether or not HAS_TYPE exists becomes difficult. Now I grant you I may be able to build a better MEMBER_TYPE based on your idea above, and I will look into it.
Hmmm..very true, I guess something like that could and will come in handy. The use case I'm thinking of is
typedef typename boost::mpl::eval_if< has_type_value_type<T>, get_type_value_type<T>, boost::mpl::identity< some_default_value_type>
::type value_type;
I think one of the things I don't like about the current setup is that there are separate HAS_TYPE and MEMBER_TYPE macros, and hence separate metafunctions, and I'd imagine implementation-wise they largely do the same thing.
MPL is very clear about not globbing more than one thing as a result of a metafunction. I agree with this.
:: nod ::
I also don't think the *name* MEMBER_TYPE / member_type_xxx really conveys
well what this macro / metafunction combination does; I don't have any alternatives at the moment.
Actually, I'm not finding it so bad anymore. But you might do well putting it into its own separate section in the documentation.
It is documented separately from the HAS_ macro metafunctions and from their equivalent nullary type metafunctions.
This might not be a good idea, but maybe it could lead a better
alternative. What if we added a nested template get to has_type_xxx that was a unary metafunction? The semantics could be
has_type_xxx<T>::template get<>::type => equivalent to T::xxx (and errors if a nested type with no such name exists) has_type_xxx<T>::template get<U>::type => if T has a nested type named xxx, evaluates to xxx, else evaluates to U
I understand this but I am not enamoured of your syntax. Let me think about it.
Yeah I'm not totally on board either. Another idea is to have a special, additional macro generate both metafunctions.
Also despite your feeling that the mullary type metafunctions should go
away
they still, in my mind, are valuable as an alternate syntax to the macro metafunctions even with your has_type extension idea.
[...]
I still feel that if I add your idea it will be added as an addition and
not as a complete replacement of anything else. My reasoning is that not everyone is as knoweldgable about MPL and as comfortable with lambda expressions as you are.
That would make 3 or 4 ways (depending on how you count it) to do the same thing (deeply nested type introspection):
1) the solution described in "Nested Types" using (only) MEMBER_TYPE / member_type_xxx; 2) the solution described in "Using Nullary Type Metafunctions" using TTI metafunctions; 3) the solution described in "Using Nullary Type Metafunctions" using TTI metafunction classes; and 4) the proposed new solution using Boost.MPL lambda expression predicates within the has_type_xxx queries.
I think, as a general rule, if you have 3 or 4 ways to do the same thing, you're asking for confusion.
We have a different philosophy about this.
First of all there are really three solutions, with your suggestion added to the two I already had. Your 2) and 3) above are hardly 'different".
Like I said, it depends on how you count. They're as different as the the metafunctions and metafunction classes are different. It's moot anyway if you're dropping the metafunction classes in favor of boost::mpl::quote[n].
Secondly MEMBER_TYPE can be used in any scenario where a nested type has to
be passed in a TTI metafunction where it may not exist. This includes as a type in member function and member data expressions in TTI. I do hear you when you say you want HAS_TYPE to also serve the purpose which MEMBER_TYPE has now so as not to have to duplicate HAS_TYPE and MEMBER_TYPE functionality.
Yeah but I'm starting to believe this duplication is inevitable.
Thirdly I do not really see where the "one way is the only right way" is best here.
I'm not saying, in general, that if there are 3 (or 4) ways to do the same thing, that one necessarily has to be the best and right way. Indeed, solution (1) is naturally possible with anything remotely like member_type_xxx, and solution (4) is naturally possible assuming has_type_xxx accepts Boost.MPL lambda expressions. Really the only argument I can make for (4) over (1) is it arguably has a more natural nesting order and it doesn't require any additional constructs beyond has_ queries.
As for (2) (say)...I think I would be more accepting of it there was just *one* generic component that wrapped metafunctions into nullary type metafunctions, rather than 10 as there are currently, and framed the use of nullary type metafunctions (i.e., fully lazy MPL) in a more general context. By itself, I don't see the value it provides. Specifically, I'm missing...
Finally, My previous two techniques, macro metafunctions and nullary type
metafunctions, are generalized ways to use TTI where the latter is merely a syntactical improvement over the former for people like me who don't like to have to grab the nested "::type" in metaprogramming constructs all the time.
...this :( You're trading "typename ... ::type" (and these are only necessary in the deeper levels of the query for solution (4) above, if you want) for mf_member_type and mf_has_whatever. See my previous email comparing the syntaxes of the various approaches (which, I admit, may have been unfair as I'm not sure if I got the other solutions right...I *think* I did, though). This trade does not seem to be an improvement, at least not syntactically. If you can convince me otherwise, I think, at a minimum, it will help the documentation. Side-by-side comparisons would certainly help.
Speaking of which, no matter how many techniques you present to perform deeply nested type introspection (or any other task), it's important to convey to the user the pros and cons of each technique, so that they can make an informed decision. Just to reiterate, I don't feel like you've made your case (after reading the documentation) that the nullary type metafunctions are a syntactic improvement, which is their raison d'etre according to the documentation.
I will think about simplifying and leaving out the nullary type metafunction syntax. Evidently they have caused consternation not only to you but others as something that is muddying the waters of what should be a basic set of functionality. In the docs, when I mentioned them, I thought I had separated them and emphasized their purely "syntactical" difference enough so that they could be skipped over by others who felt that they were not useful enough to bother with. They are not meant to be "lazy" and I did not think they would be any "slower" compile-time wise than normal metafunction syntax where one passes types around through the nested ::type member, as Dave Abrahams suggested they might be. Why they appear more convenient to me is just a personal matter. I can only liken it to how syntactically neat it is that boost::mpl passes around metafunctions rather than their underlying types or values when doing type selection and computations. I just find that passing metafunctions until one wants a final result ( whether ::type or ::value ) rather than passing the nested ::type or ::value a more pleasant syntax. So be it. I can obviously keep the nullary metafunction syntax purely for my own personal use of TTI and remove them from TTI. If I ever hear of anybody asking why their is no way of dealing with metafunctions directly when working with types in TTI I will just laugh and realize that whatever one does in programming it is impossible to please everybody. I am surprised, that given the nature of C++ as a multi-paradigm language, that there appear to be so many C++ programmers who get either confused or bothered if one presents more than one way of doing things in a C++ library. As long as documentation is clear I have never found that to be a problem when I design software.
Obviously the macor metafunctions are not going to go away as they are the
core of TTI. If nobody wants to use the nullary metafunctions except me that is fine but I see no harm in keeping them in the library
I think (hypothetically) if you're the only one who wants to use a component of your library (or of any library), generally either (a) whatever documentation on the component is not doing a good job conveying its purpose, and/or only you know how to really use it; (b) you should be using something else (i.e., everyone else has it right); or (c) your needs are (highly?) specialized. In cases (b) and (c), I think I have to seriously question whether the component is appropriate for Boost. In case (a), it's at least not ready for Boost.
Eddie

On Tue, Jul 19, 2011 at 6:23 AM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/18/2011 10:27 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
I also don't think the *name* MEMBER_TYPE / member_type_xxx really
conveys
well what this macro / metafunction combination does; I don't have any alternatives at the moment.
Actually, I'm not finding it so bad anymore. But you might do well putting it into its own separate section in the documentation.
It is documented separately from the HAS_ macro metafunctions and from their equivalent nullary type metafunctions.
Evidently I'm not using the correct quickbook or boostbook terminology for the various hierarchical constructs in documentation, but I meant as a separate *top-level* section, separate from the section summarizing the metafunction-generating macros BOOST_TTI_HAS_. I'm probably nit-picking at this point. But it might insulate those users from going through too much information if all they want to do is simple top-level introspection queries. I know when I was going through the documentation, MEMBER_TYPE / member_type_ seemed like lost and forlorn children after the big table of HAS_ / has_. Just a thought. - Jeff

On 17/07/11 04:58, Jeffrey Lee Hellrung, Jr. wrote:
* I *think* all generated metafunctions should also expose a nested "type" typedef in addition to a nested "value" static bool in order to be fully Boost.MPL compatible, but...I'm not sure on this one. For some reason, I've always followed this practice, I think because it allows has_type_xxx<T> to be used as a nullary Boost.MPL metafunction. Can someone comment on this?
Yes, could be nice to have as it will make the metafunction proper nullary MPL one. I suggest findign a way to inherit from boost::mpl::bool_ whenever possible.

On Sat, Jul 16, 2011 at 11:02 PM, Joel falcou <joel.falcou@gmail.com> wrote:
On 17/07/11 04:58, Jeffrey Lee Hellrung, Jr. wrote:
* I *think* all generated metafunctions should also expose a nested "type" typedef in addition to a nested "value" static bool in order to be fully Boost.MPL compatible, but...I'm not sure on this one. For some reason, I've always followed this practice, I think because it allows has_type_xxx<T> to be used as a nullary Boost.MPL metafunction. Can someone comment on this?
Yes, could be nice to have as it will make the metafunction proper nullary MPL one. I suggest findign a way to inherit from boost::mpl::bool_ whenever possible.
I think inheritance might be inconvenient in this case, as I'm guessing (I haven't yet looked at the implementation) the value member is computed as value = sizeof( ... ) == sizeof( ... ); Probably sufficient to BOOST_STATIC_CONSTANT( bool, value = ... ); typedef this_metafunction_name type; // 1 typedef boost::integral_constant< bool, value > type; // 2 typedef boost::mpl::bool_< value > type; // 3 where I think any of 1, 2, or 3 is fine. - Jeff

On 7/16/2011 10:58 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Thu, Jul 7, 2011 at 5:15 AM, Joel Falcou<joel.falcou@gmail.com> wrote:
The Type Traits Introspection library by Edward Diener started friday 1st have been *extended* to july 17. Comments and reviews are welcome.
[...]
Please explicitly state in your review whether the library should be accepted.
Yes.
Appreciated !
The general review checklist:
- What is your evaluation of the design?
Fundamentally sound.
- What is your evaluation of the implementation?
I will try to look into it before the end of the review; specifically, I'd like to compare it to what I have in my own code base.
- What is your evaluation of the documentation?
Generally very good. I think it will be improved if the reference sections for each component also include examples. Also, I think the reference section for each macro is not very clear. I'd prefer something like
-------- BOOST_TTI_HAS_TYPE( name )
Description
Generates a Boost.MPL-compatible metafunction boost::tti::has_type_'name', with the following semantics. --------
with a table giving all relevant expressions (e.g., boost::tti::has_type_'name'<T>::value), what the parameters are (T is any type), and what the result is (true iff T has a nested 'name' type). I think it's similarly unclear from a quick glance what the types T, U, R, etc. are in Table 1.2 in the "Macro Metafunctions" section.
I will look into improving the reference and the table.
- What is your evaluation of the potential usefulness of the library?
Indeed, very useful; I have similar such metafunction-generating macros that I have used for a while now, and I look forward to replacing them with Eddie's work.
- Did you try to use the library? With what compiler? Did you have any problems?
Not yet.
- How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
I've been nonlinearly patching together this review for a few days now, so it might come off as a bit disjointed. I apologize in advance.
- Are you knowledgeable about the problem domain?
I think so.
And finally, every review should answer this question:
- Do you think the library should be accepted as a Boost library?
[...]
Again, yes. Ideally, Eddie would accept 100% of my suggestions below, but I am a realistic person (...sometimes): (a) I could be wrong (very wrong!); and/or (b) some issues have multiple legitimate viewpoints. So all I ask is he consider my comments below.
First, a general comment. There's a lot more in this library than I expected at first, some of which is welcome but some of which I think might be unnecessary, in that, e.g., it only provides a slight convenience, or only addresses a (in my opinion) obscure use case. You (Eddie) may argue that there's no harm in including it, as users can simply ignore it, but I disagree. I believe there is value in being judicious about what to include and not include. It simplifies the presentation of the library, making it easier to digest; and it reduces maintenance.
More detailed comments follow, in roughly the order I thought of them as I read the documentation. Most of them are critical ("Dude, WTF, you should do X instead of what you're doing now"), and I apologize in advance for not mentioning the things I believe you did correctly. I just want to get straight to the point of likely the most contentious and discussable issues :)
* I would suggest a name change from "TTI" to "Introspection" or something similar. I have a feeling that, looking at a source file with "#include <boost/tti/header.hpp>" would give one less of an idea of what the purpose of that header is than "#include<boost/introspection/header.hpp>", and likewise for macros, "BOOST_TTI_MACRO" vs. "BOOST_INTROSPECTION_MACRO". I don't think the length of the name should be a huge concern since I would imagine the ratio of metafunction-generating-macro use to generated-metafunction use is much less than 1, i.e., the macro will not be used frequently relative to the metafunction that's generated.
If I changed it from TTI to something longer I will get complaints that the macro names are too long ( some are already long ). As far as the suggested "Introspection" it does seem vaguer to me than "Type Traits Introspection", byn which I meant to suggest introspecting a type.
* As others have noted and as I believe Edward has agreed to, generated metafunctions should be injected into the scope of the macro invocation, not in the boost::tti namespace.
I totally agree.
* Contrary to others opinions, I think Edward should keep both BOOST_TTI_MACRO and BOOST_TTI_TRAIT_MACRO macros (optionally renaming them, if desired; I think these are just following the Boost.MPL naming scheme, but perhaps we can think of names that evoke the difference a little more clearly). It imparts a de facto naming convention for these metafunctions. *But* you should include a warning about the possibility to automatically generate names with double underscores, and that this behavior is undefined.
I will definitely do that. BTW I thought that the double underscores were only considered reserved for the compiler when at the beginning or end of an identifier, but it seems like I am wrong ( haven't found the appropriate comment in the C++ standard ).
* As others have noted, the association between file names and the macros they define is confusing, so I think we should strive for a more predictable association.
Agreed.
* I'm confused what the purpose of the *_GEN_BASE and *_GEN macros are, and suspect they are probably unnecessary.
Once the enclosing namespace is removed, I will remove the *_GEN_BASE set and the *_GEN set will become what the current *_GEN_BASE set is. The purpose of the macro is to just generate the name of the metafunction without the end-user having to understand the naming scheme.
* I *think* all generated metafunctions should also expose a nested "type" typedef in addition to a nested "value" static bool in order to be fully Boost.MPL compatible, but...I'm not sure on this one. For some reason, I've always followed this practice, I think because it allows has_type_xxx<T> to be used as a nullary Boost.MPL metafunction. Can someone comment on this?
They do supply a nested 'type', whose 'type::value' is the same as the 'value'. I have not documented this because the nested 'type' is unimportant in using the metafunctions. The metafunction generated by BOOST_TTI_MEMBER_TYPE(name) does have just a nested 'type', which is important and used.
* I think it would be more helpful if the examples were paired with the reference section of the corresponding macro.
Do you mean a link to the appropriate reference section for the macro in the code for the examples ? Or do you mean you want an example added to reference for each macro ?
* 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).
When a nested 'type' is a typedef, the metaprogrammer may want to check if the typedef is some given 'type' That is why the "has type with check" exists.
* I like Lorenzo's idea to condense some related macros into a single macro: - I think BOOST_TTI_HAS_TEMPLATE can subsume BOOST_TTI_HAS_TEMPLATE_CHECK_PARAMS and BOOST_TTI_VM_HAS_TEMPLATE_CHECK_PARAMS.
I told Lorenzo that I will be rolling these into a BOOST_TTI_HAS_TEMPLATE for all 3 cases, which will be slightly different for variadic and non-variadic macro support.
// ugliest, but most flexible with Boost.MPL - Likewise, I think BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION can subsume BOOST_TTI_HAS_COMP_STATIC_MEMBER_FUNCTION, and likewise I think the syntax has_static_member_function_xxx< T, Return ( Arg0, Arg1 )> would be convenient. - Although this doesn't have to do with condensing macros, it would make
- I think BOOST_TTI_HAS_MEMBER_FUNCTION can subsume BOOST_TTI_HAS_COMP_MEMBER_FUNCTION, since I think you could just dispatch on whether the first template parameter to the generated metafunction is a pointer-to-member-function or not. Another convenient syntax may be has_member_function_xxx< T, Return ( Arg0, Arg1 )>, so that all of the following are equivalent: has_member_function_xxx< Return (T::*)( Arg0, Arg1 )> has_member_function_xxx< T, Return ( Arg0, Arg 1 )> // can still use lambda expressions for T, has similar format as other introspection metafunctions has_member_function_xxx< T, Return, boost::mpl::vector2< Arg0, Arg1 the library a little more consistent to allow pointer-to-member syntax for BOOST_TTI_HAS_MEMBER_DATA, e.g., has_member_data_xxx< U T::*>. - Lastly, BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION( xxx ) can be reexpressed as BOOST_TTI_HAS_MEMBER_FUNCTION( static xxx ), and likewise for BOOST_TTI_HAS_STATIC_MEMBER_DATA.
Figuring out the different template parameters and their number at compile-time may be possible. I will look into it.
* For BOOST_TTI_HAS_TEMPLATE where you supply the template signature, I'm actually fine with the old system of wrapping the signature in parentheses and replacing all commas with ")(" to make a Boost.PP Seq, and prefer it to the array syntax you've proposed since. That said, I would probably, ultimately, prefer Lorenzo's syntax (e.g., "TTI_HAS_TEMPLATE( template( class, class ) struct tmpl )"), but I guess we'll agree to disagree on what's easier to read (readability should win over writability, right?). For the syntax taking advantage of variadic macros that you've proposed during the review (e.g., "TTI_HAS_TEMPLATE( tmpl, template< class, class> )"), I worry about usability issues when you're just stringing the template signature into the __VA_ARGS__ argument. I don't use variadic macros yet, so my sense of "good practice" is very malleable at the moment, but, that being said, it seems like good practice is to use variadic macro arguments only when (a) your macro logically takes a fixed number of arguments, but where you can syntactically leave off trailing arguments that you wish to be defaulted; or (b) your macro takes a varying number of arguments, but the semantic difference between each argument is purely and strictly positional. To me, the template signature is a completely separate argument from the template name, and the syntax should reflect that, e.g., TTI_HAS_TEMPLATE( tmpl, (template< class, class>) ) or TTI_HAS_TEMPLATE( tmpl, ( class, class ) ). It also seems like this would make it easier to use TTI_HAS_TEMPLATE within other preprocessor metaprogramming constructs (e.g., if I wanted to generate an entire family of metafunctions via BOOST_PP_REPEAT or BOOST_PP_ITERATE), but perhaps you can argue otherwise.
I will have to disagree with your belief that variadic macro syntax should not be used.
* When introspecting for nested templates and member functions, how "precise" is the result? I.e., if you're checking if T has member function xxx with signature void ( ), and T in fact has a member function xxx with signature void ( int = 0 ), is the result false? (I'm guessing yes, and this should be noted in the reference.) Similarly for nested templates with default template parameters.
For functions and data the precision should match that of the compiler without considering default value(s). 'void ( int = 0 )' still has a basic signature of 'void (int) and not 'void ()' so the result should be false. But you have alerted me to provide tests to check out function signatures with default values.
[The next three comments have been echoed by others.] * Please document is how the accessibility of the inner elements affects whether metafunction invocations can be compile and what their result is.
I will do that.
* Please document in which scopes you may invoke the metafunction-generating macros.
I will do that but let me just reiterate that the metafunction-generating macros generate metafunctions, and therefore anywhere a metafunction can be used is where the macros can be used.
* Please provide rationale for why you support introspection some properties but not others (e.g., namespace introspection, virtual functions). I assume this is a limitation of the C++ language.
* The use case you give to motivate BOOST_TTI_MEMBER_TYPE and valid_member_type seems somewhat contrived. I have never run into this situation further than nesting 1 level deep. However, assuming the validity of the use case, if you have a has_type_xxx< T, Predicate> metafunction, you can determine whether T::AType::BType::CType::FindType exists without a compiler the error in the event that one of the intermediate types doesn't exist via
has_type_AType< T, has_type_BType< boost::mpl::_1, has_type_CType< boost::mpl::_1, has_type_FindType< boost::mpl::_1> > >
Oops, okay, that won't work without some additional boost::mpl::protect's, but hopefully you get the idea. Let me know if you want me to elaborate.
Please elaborate.
* Similar to above, I'm not convinced of the utility of the metafunction-class-generated macros...aren't they largely obviated by boost::mpl::quote[n]?
I created them for convenience, because I found them easy to use as an alternative. I do understand now that quote can be used instead, as well of course as placeholder expressions. If there is a strong feeling from others that I should remove the MTFC macros I will do so.
* Lastly, and I'm not absolutely sure about this, but it seems like adding metafunction predicate parameters to the has_type_xxx generated metafunctions would obviate the need for the nullary metafunction infrastructure. I can elaborate on this further if you don't understand what I mean.
Please do elaborate. When I created the nullary metafunctions, as a syntax convenience, I was looking for another way but did not find one.
* It's *awesome* that there's an index...generated by John Maddock's AutoIndex tool?
Yes.
I'm open to discussing the above issues beyond the review period, so, Eddie, you may respond at your convenience.
Feel free to comment further. Eddie

On Sun, Jul 17, 2011 at 8:10 AM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/16/2011 10:58 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
First, a general comment. There's a lot more in this library than I
expected at first, some of which is welcome but some of which I think might be unnecessary, in that, e.g., it only provides a slight convenience, or only addresses a (in my opinion) obscure use case. You (Eddie) may argue that there's no harm in including it, as users can simply ignore it, but I disagree. I believe there is value in being judicious about what to include and not include. It simplifies the presentation of the library, making it easier to digest; and it reduces maintenance.
I think I'll have to reference these comments below.
* I would suggest a name change from "TTI" to "Introspection" or something
similar. I have a feeling that, looking at a source file with "#include <boost/tti/header.hpp>" would give one less of an idea of what the purpose of that header is than "#include<boost/introspection/header.hpp>", and likewise for macros, "BOOST_TTI_MACRO" vs. "BOOST_INTROSPECTION_MACRO". I don't think the length of the name should be a huge concern since I would imagine the ratio of metafunction-generating-macro use to generated-metafunction use is much less than 1, i.e., the macro will not be used frequently relative to the metafunction that's generated.
If I changed it from TTI to something longer I will get complaints that the macro names are too long ( some are already long ). As far as the suggested "Introspection" it does seem vaguer to me than "Type Traits Introspection", byn which I meant to suggest introspecting a type.
Yeah, I understand on both accounts. Maybe I'm just being paranoid.
* I'm confused what the purpose of the *_GEN_BASE and *_GEN macros are,
and suspect they are probably unnecessary.
Once the enclosing namespace is removed, I will remove the *_GEN_BASE set and the *_GEN set will become what the current *_GEN_BASE set is. The purpose of the macro is to just generate the name of the metafunction without the end-user having to understand the naming scheme.
Well, if the naming scheme is simple (I think it is), I don't think we have to worry about the metafunction provider nor the metafunction user not understanding the naming scheme. Have users been requesting these GEN macros? Are the GEN macros targeted toward the metafunction provider (who should know what identifiers he's injecting into the namespace) or the metafunction user (who will need to read documentation either way to get the name of the metafunction)? I don't really see the point, and I now refer you to my comment about "less is more" at the top. * I *think* all generated metafunctions should also expose a nested "type"
typedef in addition to a nested "value" static bool in order to be fully Boost.MPL compatible, but...I'm not sure on this one. For some reason, I've always followed this practice, I think because it allows has_type_xxx<T> to be used as a nullary Boost.MPL metafunction. Can someone comment on this?
They do supply a nested 'type', whose 'type::value' is the same as the 'value'. I have not documented this because the nested 'type' is unimportant in using the metafunctions. The metafunction generated by BOOST_TTI_MEMBER_TYPE(name) does have just a nested 'type', which is important and used.
Okay, as I hadn't looked yet at the implementation, I didn't know it had a nested type. It *is* worth documenting, I think.
* I think it would be more helpful if the examples were paired with the reference section of the corresponding macro.
Do you mean a link to the appropriate reference section for the macro in the code for the examples ? Or do you mean you want an example added to reference for each macro ?
The latter, e.g., as is done in the Boost.MPL documentation.
* 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).
When a nested 'type' is a typedef, the metaprogrammer may want to check if the typedef is some given 'type' That is why the "has type with check" exists.
I understand; perhaps I should've explicitly asked: Why are you limiting it to that particular query? Surely the metaprogrammer may want to check if the typedef has other properties as well, no? * I like Lorenzo's idea to condense some related macros into a single
macro: - I think BOOST_TTI_HAS_TEMPLATE can subsume BOOST_TTI_HAS_TEMPLATE_CHECK_**PARAMS and BOOST_TTI_VM_HAS_TEMPLATE_**CHECK_PARAMS.
I told Lorenzo that I will be rolling these into a BOOST_TTI_HAS_TEMPLATE for all 3 cases, which will be slightly different for variadic and non-variadic macro support.
Good.
- I think BOOST_TTI_HAS_MEMBER_FUNCTION can subsume
BOOST_TTI_HAS_COMP_MEMBER_**FUNCTION, since I think you could just dispatch on whether the first template parameter to the generated metafunction is a pointer-to-member-function or not. Another convenient syntax may be has_member_function_xxx< T, Return ( Arg0, Arg1 )>, so that all of the following are equivalent: has_member_function_xxx< Return (T::*)( Arg0, Arg1 )> has_member_function_xxx< T, Return ( Arg0, Arg 1 )> // can still use lambda expressions for T, has similar format as other introspection metafunctions has_member_function_xxx< T, Return, boost::mpl::vector2< Arg0, Arg1
// ugliest, but most flexible with Boost.MPL
- Likewise, I think BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION can subsume BOOST_TTI_HAS_COMP_STATIC_**MEMBER_FUNCTION, and likewise I think the syntax has_static_member_function_**xxx< T, Return ( Arg0, Arg1 )> would be convenient. - Although this doesn't have to do with condensing macros, it would make the library a little more consistent to allow pointer-to-member syntax for BOOST_TTI_HAS_MEMBER_DATA, e.g., has_member_data_xxx< U T::*>. - Lastly, BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION( xxx ) can be reexpressed as BOOST_TTI_HAS_MEMBER_FUNCTION( static xxx ), and likewise for BOOST_TTI_HAS_STATIC_MEMBER_**DATA.
Figuring out the different template parameters and their number at compile-time may be possible. I will look into it.
Looks like it would be a simple matter of dispatching on is_member_function_pointer, is_function, and is_member_object_pointer from Boost.TypeTraits. Oops, I just found an inconsistency between your terminology (member data) and Boost.TypeTraits (member object)...
* For BOOST_TTI_HAS_TEMPLATE where you supply the template signature, I'm
actually fine with the old system of wrapping the signature in parentheses and replacing all commas with ")(" to make a Boost.PP Seq, and prefer it to the array syntax you've proposed since. That said, I would probably, ultimately, prefer Lorenzo's syntax (e.g., "TTI_HAS_TEMPLATE( template( class, class ) struct tmpl )"), but I guess we'll agree to disagree on what's easier to read (readability should win over writability, right?). For the syntax taking advantage of variadic macros that you've proposed during the review (e.g., "TTI_HAS_TEMPLATE( tmpl, template< class, class> )"), I worry about usability issues when you're just stringing the template signature into the __VA_ARGS__ argument. I don't use variadic macros yet, so my sense of "good practice" is very malleable at the moment, but, that being said, it seems like good practice is to use variadic macro arguments only when (a) your macro logically takes a fixed number of arguments, but where you can syntactically leave off trailing arguments that you wish to be defaulted; or (b) your macro takes a varying number of arguments, but the semantic difference between each argument is purely and strictly positional. To me, the template signature is a completely separate argument from the template name, and the syntax should reflect that, e.g., TTI_HAS_TEMPLATE( tmpl, (template< class, class>) ) or TTI_HAS_TEMPLATE( tmpl, ( class, class ) ). It also seems like this would make it easier to use TTI_HAS_TEMPLATE within other preprocessor metaprogramming constructs (e.g., if I wanted to generate an entire family of metafunctions via BOOST_PP_REPEAT or BOOST_PP_ITERATE), but perhaps you can argue otherwise.
I will have to disagree with your belief that variadic macro syntax should not be used.
:: grumble ::
* When introspecting for nested templates and member functions, how
"precise" is the result? I.e., if you're checking if T has member function xxx with signature void ( ), and T in fact has a member function xxx with signature void ( int = 0 ), is the result false? (I'm guessing yes, and this should be noted in the reference.) Similarly for nested templates with default template parameters.
For functions and data the precision should match that of the compiler without considering default value(s). 'void ( int = 0 )' still has a basic signature of 'void (int) and not 'void ()' so the result should be false. But you have alerted me to provide tests to check out function signatures with default values.
I presume templates with default values is the same deal. I also presume, from your preceding comment, that checking compatible template signatures is not supported. E.g., I want to check if T::template tmpl<0> is syntactically well-formed, which it would be if T had a nested template declared as template< int, class = void > struct tmpl or template< unsigned int > struct tmpl, but I suspect that TTI does not provide the facilities to do this. * Please document in which scopes you may invoke the
metafunction-generating macros.
I will do that but let me just reiterate that the metafunction-generating macros generate metafunctions, and therefore anywhere a metafunction can be used is where the macros can be used.
You mean, "anywhere a metafunction can be *defined* is where the macros can be used", correct? I wasn't sure if you were, e.g., creating dummy namespaces with the macro, which would limit the metafunction-generating macros to namespace scope. * Similar to above, I'm not convinced of the utility of the
metafunction-class-generated macros...aren't they largely obviated by boost::mpl::quote[n]?
I created them for convenience, because I found them easy to use as an alternative. I do understand now that quote can be used instead, as well of course as placeholder expressions.
'quote'ing them should address any compile-time efficiency concerns. If there is a strong feeling from others that I should remove the MTFC
macros I will do so.
Let me again refer you to my "less is more" comment at the top. - Jeff

On 7/17/2011 2:22 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 8:10 AM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/16/2011 10:58 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
First, a general comment. There's a lot more in this library than I
expected at first, some of which is welcome but some of which I think might be unnecessary, in that, e.g., it only provides a slight convenience, or only addresses a (in my opinion) obscure use case. You (Eddie) may argue that there's no harm in including it, as users can simply ignore it, but I disagree. I believe there is value in being judicious about what to include and not include. It simplifies the presentation of the library, making it easier to digest; and it reduces maintenance.
I think I'll have to reference these comments below.
* I would suggest a name change from "TTI" to "Introspection" or something
similar. I have a feeling that, looking at a source file with "#include <boost/tti/header.hpp>" would give one less of an idea of what the purpose of that header is than "#include<boost/introspection/header.hpp>", and likewise for macros, "BOOST_TTI_MACRO" vs. "BOOST_INTROSPECTION_MACRO". I don't think the length of the name should be a huge concern since I would imagine the ratio of metafunction-generating-macro use to generated-metafunction use is much less than 1, i.e., the macro will not be used frequently relative to the metafunction that's generated.
If I changed it from TTI to something longer I will get complaints that the macro names are too long ( some are already long ). As far as the suggested "Introspection" it does seem vaguer to me than "Type Traits Introspection", byn which I meant to suggest introspecting a type.
Yeah, I understand on both accounts. Maybe I'm just being paranoid.
* I'm confused what the purpose of the *_GEN_BASE and *_GEN macros are,
and suspect they are probably unnecessary.
Once the enclosing namespace is removed, I will remove the *_GEN_BASE set and the *_GEN set will become what the current *_GEN_BASE set is. The purpose of the macro is to just generate the name of the metafunction without the end-user having to understand the naming scheme.
Well, if the naming scheme is simple (I think it is), I don't think we have to worry about the metafunction provider nor the metafunction user not understanding the naming scheme. Have users been requesting these GEN macros? Are the GEN macros targeted toward the metafunction provider (who should know what identifiers he's injecting into the namespace) or the metafunction user (who will need to read documentation either way to get the name of the metafunction)? I don't really see the point, and I now refer you to my comment about "less is more" at the top.
The GEN macros are targeted at the metafunction user. If people really feel it is easier reading the documentation and looking at the way that metafunction names are generated than to just repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name, I may remove the GEN macros. I still think that using the equivalent GEN macro for each metafunction macro is a nice and easy way to get the refer to the name of the metafunction for each metafunction macro.
* I *think* all generated metafunctions should also expose a nested "type"
typedef in addition to a nested "value" static bool in order to be fully Boost.MPL compatible, but...I'm not sure on this one. For some reason, I've always followed this practice, I think because it allows has_type_xxx<T> to be used as a nullary Boost.MPL metafunction. Can someone comment on this?
They do supply a nested 'type', whose 'type::value' is the same as the 'value'. I have not documented this because the nested 'type' is unimportant in using the metafunctions. The metafunction generated by BOOST_TTI_MEMBER_TYPE(name) does have just a nested 'type', which is important and used.
Okay, as I hadn't looked yet at the implementation, I didn't know it had a nested type. It *is* worth documenting, I think.
OK, I can do it. Since I keep saying "metafunction" in the documentation I admit I am surprised that others did not assume that I am generation a real metafunction, ie. that it must have a nested 'type'.
* I think it would be more helpful if the examples were paired with the reference section of the corresponding macro.
Do you mean a link to the appropriate reference section for the macro in the code for the examples ? Or do you mean you want an example added to reference for each macro ?
OK, I understand. This will be all part of my focusing more on each individual metafunction usage in my doc.
The latter, e.g., as is done in the Boost.MPL documentation.
* 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).
When a nested 'type' is a typedef, the metaprogrammer may want to check if the typedef is some given 'type' That is why the "has type with check" exists.
I understand; perhaps I should've explicitly asked: Why are you limiting it to that particular query? Surely the metaprogrammer may want to check if the typedef has other properties as well, no?
What other properties can a typedef have ? In C++ it is just an alias 'name' for a type.
* I like Lorenzo's idea to condense some related macros into a single
macro: - I think BOOST_TTI_HAS_TEMPLATE can subsume BOOST_TTI_HAS_TEMPLATE_CHECK_**PARAMS and BOOST_TTI_VM_HAS_TEMPLATE_**CHECK_PARAMS.
I told Lorenzo that I will be rolling these into a BOOST_TTI_HAS_TEMPLATE for all 3 cases, which will be slightly different for variadic and non-variadic macro support.
Good.
- I think BOOST_TTI_HAS_MEMBER_FUNCTION can subsume
BOOST_TTI_HAS_COMP_MEMBER_**FUNCTION, since I think you could just dispatch on whether the first template parameter to the generated metafunction is a pointer-to-member-function or not. Another convenient syntax may be has_member_function_xxx< T, Return ( Arg0, Arg1 )>, so that all of the following are equivalent: has_member_function_xxx< Return (T::*)( Arg0, Arg1 )> has_member_function_xxx< T, Return ( Arg0, Arg 1 )> // can still use lambda expressions for T, has similar format as other introspection metafunctions has_member_function_xxx< T, Return, boost::mpl::vector2< Arg0, Arg1
// ugliest, but most flexible with Boost.MPL
- Likewise, I think BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION can subsume BOOST_TTI_HAS_COMP_STATIC_**MEMBER_FUNCTION, and likewise I think the syntax has_static_member_function_**xxx< T, Return ( Arg0, Arg1 )> would be convenient. - Although this doesn't have to do with condensing macros, it would make the library a little more consistent to allow pointer-to-member syntax for BOOST_TTI_HAS_MEMBER_DATA, e.g., has_member_data_xxx< U T::*>. - Lastly, BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION( xxx ) can be reexpressed as BOOST_TTI_HAS_MEMBER_FUNCTION( static xxx ), and likewise for BOOST_TTI_HAS_STATIC_MEMBER_**DATA.
Figuring out the different template parameters and their number at compile-time may be possible. I will look into it.
Looks like it would be a simple matter of dispatching on is_member_function_pointer, is_function, and is_member_object_pointer from Boost.TypeTraits.
It is little more complicated than that. The second parameter ( the first is the enclosing type ) in the non-composite form is the return type. A return type could be a member function pointer itself etc., right ?
Oops, I just found an inconsistency between your terminology (member data) and Boost.TypeTraits (member object)...
I am pretty comfortable with member data, but really I do not think there is any consistent name for this in C++ terminologies. Besides this has already been discussed in other TTI revew threads pretty extensively.
* For BOOST_TTI_HAS_TEMPLATE where you supply the template signature, I'm
actually fine with the old system of wrapping the signature in parentheses and replacing all commas with ")(" to make a Boost.PP Seq, and prefer it to the array syntax you've proposed since. That said, I would probably, ultimately, prefer Lorenzo's syntax (e.g., "TTI_HAS_TEMPLATE( template( class, class ) struct tmpl )"), but I guess we'll agree to disagree on what's easier to read (readability should win over writability, right?). For the syntax taking advantage of variadic macros that you've proposed during the review (e.g., "TTI_HAS_TEMPLATE( tmpl, template< class, class> )"), I worry about usability issues when you're just stringing the template signature into the __VA_ARGS__ argument. I don't use variadic macros yet, so my sense of "good practice" is very malleable at the moment, but, that being said, it seems like good practice is to use variadic macro arguments only when (a) your macro logically takes a fixed number of arguments, but where you can syntactically leave off trailing arguments that you wish to be defaulted; or (b) your macro takes a varying number of arguments, but the semantic difference between each argument is purely and strictly positional. To me, the template signature is a completely separate argument from the template name, and the syntax should reflect that, e.g., TTI_HAS_TEMPLATE( tmpl, (template< class, class>) ) or TTI_HAS_TEMPLATE( tmpl, ( class, class ) ). It also seems like this would make it easier to use TTI_HAS_TEMPLATE within other preprocessor metaprogramming constructs (e.g., if I wanted to generate an entire family of metafunctions via BOOST_PP_REPEAT or BOOST_PP_ITERATE), but perhaps you can argue otherwise.
I will have to disagree with your belief that variadic macro syntax should not be used.
:: grumble ::
* When introspecting for nested templates and member functions, how
"precise" is the result? I.e., if you're checking if T has member function xxx with signature void ( ), and T in fact has a member function xxx with signature void ( int = 0 ), is the result false? (I'm guessing yes, and this should be noted in the reference.) Similarly for nested templates with default template parameters.
For functions and data the precision should match that of the compiler without considering default value(s). 'void ( int = 0 )' still has a basic signature of 'void (int) and not 'void ()' so the result should be false. But you have alerted me to provide tests to check out function signatures with default values.
I presume templates with default values is the same deal.
Yes.
I also presume, from your preceding comment, that checking compatible template signatures is not supported. E.g., I want to check if T::template tmpl<0> is syntactically well-formed, which it would be if T had a nested template declared as template< int, class = void> struct tmpl or template< unsigned int> struct tmpl, but I suspect that TTI does not provide the facilities to do this.
No it does not provide such a possibility. Maybe it can be done but I do not think so right now that it is possible. Strangely enough, if gcc and VC++ were not broken one could test the instantiation of a nested function template.
* Please document in which scopes you may invoke the
metafunction-generating macros.
I will do that but let me just reiterate that the metafunction-generating macros generate metafunctions, and therefore anywhere a metafunction can be used is where the macros can be used.
You mean, "anywhere a metafunction can be *defined* is where the macros can be used", correct? I wasn't sure if you were, e.g., creating dummy namespaces with the macro, which would limit the metafunction-generating macros to namespace scope.
Actually I had been creating code in a boost::tti::detail namespace, but when I drop generating a namespace I will have to come up with another scheme. I am glad you brought this up. I can still add some fixed boost::tti::detail functionality I am need via the header file inclusion.
* Similar to above, I'm not convinced of the utility of the
metafunction-class-generated macros...aren't they largely obviated by boost::mpl::quote[n]?
I created them for convenience, because I found them easy to use as an alternative. I do understand now that quote can be used instead, as well of course as placeholder expressions.
'quote'ing them should address any compile-time efficiency concerns.
If there is a strong feeling from others that I should remove the MTFC
macros I will do so.
Let me again refer you to my "less is more" comment at the top.
The fact that one can use boost::mpl::quote has convinced me to drop the MTFC macros unless I hear from others that they are still convenient. I will document the use of boost::mpl::quote as an alternative to placeholders in discussing passing the macro metafunctions as data.

On Sun, Jul 17, 2011 at 12:35 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/17/2011 2:22 PM, Jeffrey Lee Hellrung, Jr. wrote:
Well, if the naming scheme is simple (I think it is), I don't think we have to worry about the metafunction provider nor the metafunction user not understanding the naming scheme. Have users been requesting these GEN macros? Are the GEN macros targeted toward the metafunction provider (who should know what identifiers he's injecting into the namespace) or the metafunction user (who will need to read documentation either way to get the name of the metafunction)? I don't really see the point, and I now refer you to my comment about "less is more" at the top.
The GEN macros are targeted at the metafunction user. If people really feel it is easier reading the documentation and looking at the way that metafunction names are generated than to just repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name, I may remove the GEN macros. I still think that using the equivalent GEN macro for each metafunction macro is a nice and easy way to get the refer to the name of the metafunction for each metafunction macro.
They'd have to read the documentation to figure out how to use the GEN macros, too :) Also "reading the documentation and looking at the way that metafunction names are generated" is 13 words worth work, while "repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name" is 20 words worth work, so the former is by far easier than the latter :) I'll go with whatever the general consensus is which comes out of the review. They do supply a nested 'type', whose 'type::value' is the same as the
'value'. I have not documented this because the nested 'type' is unimportant in using the metafunctions. The metafunction generated by BOOST_TTI_MEMBER_TYPE(name) does have just a nested 'type', which is important and used.
Okay, as I hadn't looked yet at the implementation, I didn't know it had a nested type. It *is* worth documenting, I think.
OK, I can do it. Since I keep saying "metafunction" in the documentation I admit I am surprised that others did not assume that I am generation a real metafunction, ie. that it must have a nested 'type'.
That is a good point regarding your use of the term "metafunction". I think it was only confusing because you mentioned the value member, hence one concludes that the value member is the only part of the public API.
I understand; perhaps I should've explicitly asked: Why are you limiting
it to that particular query? Surely the metaprogrammer may want to check if the typedef has other properties as well, no?
What other properties can a typedef have ? In C++ it is just an alias 'name' for a type.
Sorry: "Surely the metaprogrammer may want to check if the type has other properties as well, no?"
- I think BOOST_TTI_HAS_MEMBER_FUNCTION can subsume
BOOST_TTI_HAS_COMP_MEMBER_****FUNCTION, since I think you could just dispatch on whether the first template parameter to the generated metafunction is a pointer-to-member-function or not. Another convenient syntax may be has_member_function_xxx< T, Return ( Arg0, Arg1 )>, so that all of the following are equivalent: has_member_function_xxx< Return (T::*)( Arg0, Arg1 )> has_member_function_xxx< T, Return ( Arg0, Arg 1 )> // can still use lambda expressions for T, has similar format as other introspection metafunctions has_member_function_xxx< T, Return, boost::mpl::vector2< Arg0, Arg1
// ugliest, but most flexible with Boost.MPL
- Likewise, I think BOOST_TTI_HAS_STATIC_MEMBER_****FUNCTION can
subsume BOOST_TTI_HAS_COMP_STATIC_****MEMBER_FUNCTION, and likewise I think the syntax has_static_member_function_****xxx< T, Return ( Arg0, Arg1 )> would be convenient. - Although this doesn't have to do with condensing macros, it would make the library a little more consistent to allow pointer-to-member syntax for BOOST_TTI_HAS_MEMBER_DATA, e.g., has_member_data_xxx< U T::*>. - Lastly, BOOST_TTI_HAS_STATIC_MEMBER_****FUNCTION( xxx ) can be reexpressed as BOOST_TTI_HAS_MEMBER_FUNCTION( static xxx ), and likewise for BOOST_TTI_HAS_STATIC_MEMBER_****DATA.
Figuring out the different template parameters and their number at compile-time may be possible. I will look into it.
Looks like it would be a simple matter of dispatching on is_member_function_pointer, is_function, and is_member_object_pointer from Boost.TypeTraits.
It is little more complicated than that. The second parameter ( the first is the enclosing type ) in the non-composite form is the return type. A return type could be a member function pointer itself etc., right ?
I think you can dispatch based on (a) the first template parameter is a member function pointer (or member data pointer for MEMBER_DATA) (b) otherwise, the first template parameter is the enclosing type (i) the second template parameter is a function type (ii) the second template parameter is a not a function type, in which case it is the result type of the member function - the third parameter is a Boost.MPL sequence, in which case it specifies the parameter types of the member function - the last parameter, if present, is the Boost.FunctionTypes tag type [And WTH (W = Why) am I getting random asterisks put in my replies???]
Oops, I just found an inconsistency between your
terminology (member data) and Boost.TypeTraits (member object)...
I am pretty comfortable with member data, but really I do not think there is any consistent name for this in C++ terminologies. Besides this has already been discussed in other TTI revew threads pretty extensively.
I am also fine with the TTI terminology, I just wish there was uniformity :/
I also presume,
from your preceding comment, that checking compatible template signatures is not supported. E.g., I want to check if T::template tmpl<0> is syntactically well-formed, which it would be if T had a nested template declared as template< int, class = void> struct tmpl or template< unsigned int> struct tmpl, but I suspect that TTI does not provide the facilities to do this.
No it does not provide such a possibility. Maybe it can be done but I do not think so right now that it is possible.
I seem to remember that the Boost.MPL metafunction-generating macro for introspecting templates detected "variadic templates" (in this context, I mean templates with only type parameters, some of which may be defaulted); is this accurate? This is basically what the BOOST_TTI_HAS_TEMPLATE macro detects, right? I should look through the implementation :/ Strangely enough, if gcc and VC++ were not broken one could test the
instantiation of a nested function template.
Right, I read that section.
Actually I had been creating code in a boost::tti::detail namespace, but when I drop generating a namespace I will have to come up with another scheme. I am glad you brought this up. I can still add some fixed boost::tti::detail functionality I am need via the header file inclusion.
Right, and maybe put the per-metafunction-specific stuff in the private section of the metafunction, if possible... Although limited the macro invocations to namespace scope isn't a huge limitation, I don't think. - Jeff

On 7/17/2011 4:15 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 12:35 PM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/17/2011 2:22 PM, Jeffrey Lee Hellrung, Jr. wrote:
Well, if the naming scheme is simple (I think it is), I don't think we have to worry about the metafunction provider nor the metafunction user not understanding the naming scheme. Have users been requesting these GEN macros? Are the GEN macros targeted toward the metafunction provider (who should know what identifiers he's injecting into the namespace) or the metafunction user (who will need to read documentation either way to get the name of the metafunction)? I don't really see the point, and I now refer you to my comment about "less is more" at the top.
The GEN macros are targeted at the metafunction user. If people really feel it is easier reading the documentation and looking at the way that metafunction names are generated than to just repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name, I may remove the GEN macros. I still think that using the equivalent GEN macro for each metafunction macro is a nice and easy way to get the refer to the name of the metafunction for each metafunction macro.
They'd have to read the documentation to figure out how to use the GEN macros, too :) Also "reading the documentation and looking at the way that metafunction names are generated" is 13 words worth work, while "repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name" is 20 words worth work, so the former is by far easier than the latter :)
I thought it would be easier, given let's say macro BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION(name), to refer to the metafunction name as BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION_GEN(name) than to remember it is: 'has_static_member_function_name'. I admit I can be wrong about this but I really, really do not see what harm those GEN macros can cause for those who want to use them instead.
I'll go with whatever the general consensus is which comes out of the review.
They do supply a nested 'type', whose 'type::value' is the same as the
'value'. I have not documented this because the nested 'type' is unimportant in using the metafunctions. The metafunction generated by BOOST_TTI_MEMBER_TYPE(name) does have just a nested 'type', which is important and used.
Okay, as I hadn't looked yet at the implementation, I didn't know it had a nested type. It *is* worth documenting, I think.
OK, I can do it. Since I keep saying "metafunction" in the documentation I admit I am surprised that others did not assume that I am generation a real metafunction, ie. that it must have a nested 'type'.
That is a good point regarding your use of the term "metafunction". I think it was only confusing because you mentioned the value member, hence one concludes that the value member is the only part of the public API.
I understand; perhaps I should've explicitly asked: Why are you limiting
it to that particular query? Surely the metaprogrammer may want to check if the typedef has other properties as well, no?
What other properties can a typedef have ? In C++ it is just an alias 'name' for a type.
Sorry: "Surely the metaprogrammer may want to check if the type has other properties as well, no?"
I understand now where you are going with this ( see my answer to your other post ).
- I think BOOST_TTI_HAS_MEMBER_FUNCTION can subsume
BOOST_TTI_HAS_COMP_MEMBER_****FUNCTION, since I think you could just dispatch on whether the first template parameter to the generated metafunction is a pointer-to-member-function or not. Another convenient syntax may be has_member_function_xxx< T, Return ( Arg0, Arg1 )>, so that all of the following are equivalent: has_member_function_xxx< Return (T::*)( Arg0, Arg1 )> has_member_function_xxx< T, Return ( Arg0, Arg 1 )> // can still use lambda expressions for T, has similar format as other introspection metafunctions has_member_function_xxx< T, Return, boost::mpl::vector2< Arg0, Arg1
// ugliest, but most flexible with Boost.MPL
> > - Likewise, I think BOOST_TTI_HAS_STATIC_MEMBER_****FUNCTION can
subsume BOOST_TTI_HAS_COMP_STATIC_****MEMBER_FUNCTION, and likewise I think the syntax has_static_member_function_****xxx< T, Return ( Arg0, Arg1 )> would be convenient. - Although this doesn't have to do with condensing macros, it would make the library a little more consistent to allow pointer-to-member syntax for BOOST_TTI_HAS_MEMBER_DATA, e.g., has_member_data_xxx< U T::*>. - Lastly, BOOST_TTI_HAS_STATIC_MEMBER_****FUNCTION( xxx ) can be reexpressed as BOOST_TTI_HAS_MEMBER_FUNCTION( static xxx ), and likewise for BOOST_TTI_HAS_STATIC_MEMBER_****DATA.
Figuring out the different template parameters and their number at compile-time may be possible. I will look into it.
Looks like it would be a simple matter of dispatching on is_member_function_pointer, is_function, and is_member_object_pointer from Boost.TypeTraits.
It is little more complicated than that. The second parameter ( the first is the enclosing type ) in the non-composite form is the return type. A return type could be a member function pointer itself etc., right ?
I think you can dispatch based on (a) the first template parameter is a member function pointer (or member data pointer for MEMBER_DATA)
This means switching the first two parameters when specifying the composite syntax. I am not comfortable with that only because the library regularizes on the notion that the first template parameter is the enclosing type. Otherwise your idea below will work.
(b) otherwise, the first template parameter is the enclosing type (i) the second template parameter is a function type (ii) the second template parameter is a not a function type, in which case it is the result type of the member function - the third parameter is a Boost.MPL sequence, in which case it specifies the parameter types of the member function - the last parameter, if present, is the Boost.FunctionTypes tag type
I think I can get the arity of the template at compile time and work with that instead to start.
[And WTH (W = Why) am I getting random asterisks put in my replies???]
Oops, I just found an inconsistency between your
terminology (member data) and Boost.TypeTraits (member object)...
I am pretty comfortable with member data, but really I do not think there is any consistent name for this in C++ terminologies. Besides this has already been discussed in other TTI revew threads pretty extensively.
I am also fine with the TTI terminology, I just wish there was uniformity :/
I also presume,
from your preceding comment, that checking compatible template signatures is not supported. E.g., I want to check if T::template tmpl<0> is syntactically well-formed, which it would be if T had a nested template declared as template< int, class = void> struct tmpl or template< unsigned int> struct tmpl, but I suspect that TTI does not provide the facilities to do this.
No it does not provide such a possibility. Maybe it can be done but I do not think so right now that it is possible.
I seem to remember that the Boost.MPL metafunction-generating macro for introspecting templates detected "variadic templates" (in this context, I mean templates with only type parameters, some of which may be defaulted); is this accurate?
Yes.
This is basically what the BOOST_TTI_HAS_TEMPLATE macro detects, right?
Yes, I am just reusing the MPL macro under the table.
I should look through the implementation :/
Granted. I understand it and its technique pretty well, else I could not have filtered out from it my exact template matching code.
Strangely enough, if gcc and VC++ were not broken one could test the
instantiation of a nested function template.
Right, I read that section.
I am glad to hear someone has read it. I was so happy to find that method and then really disappointed when both gcc and VC++ proved broken. I could still offer it for clang and other compilers when they get fixed eventually. I have gotten e-mail from gcc saying that the target for fixing it is gcc-4.6.3. For Microsoft they are still in denial but Stephan Lavavej told me he has reopened my bug report internally.
Actually I had been creating code in a boost::tti::detail namespace, but when I drop generating a namespace I will have to come up with another scheme. I am glad you brought this up. I can still add some fixed boost::tti::detail functionality I am need via the header file inclusion.
Right, and maybe put the per-metafunction-specific stuff in the private section of the metafunction, if possible...
Yes. It is eminently doable. i just have to be correct and not cause any problems. Not putting TTI in a namespace successfully will be my first update to it once the dust clears after the review.
Although limited the macro invocations to namespace scope isn't a huge limitation, I don't think.
Possibly, but someone has already expressed the fact that they may want to use TTI within a class. I did not ask for a use case. Eddie

On Sun, Jul 17, 2011 at 3:18 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/17/2011 4:15 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 12:35 PM, Edward Diener<eldiener@tropicsoft.com
wrote:
On 7/17/2011 2:22 PM, Jeffrey Lee Hellrung, Jr. wrote:
Looks like it would be a simple matter of dispatching on
is_member_function_pointer, is_function, and is_member_object_pointer from Boost.TypeTraits.
It is little more complicated than that. The second parameter ( the first is the enclosing type ) in the non-composite form is the return type. A return type could be a member function pointer itself etc., right ?
I think you can dispatch based on (a) the first template parameter is a member function pointer (or member data pointer for MEMBER_DATA)
This means switching the first two parameters when specifying the composite syntax. I am not comfortable with that only because the library regularizes on the notion that the first template parameter is the enclosing type. Otherwise your idea below will work.
Whoops, I had understood the composite syntax to allow you to do has_member_function_xxx< Result (T::*)( Arg0, Arg1 ) > but I see from the documentation of HAS_COMP_MEMBER_FUNCTION that it isn't the case. So why not support the above syntax instead? I don't see the point in specifying the enclosing class type twice. To reiterate what I had above, I propose the following to all be equivalent: has_member_function_xxx< Result (T::*)( Arg0, Arg1 ) > has_member_function_xxx< T, Result ( Arg0, Arg1 ) > // my personal preference, probably has_member_function_xxx< T, Result, boost::mpl::vector2< Arg0, Arg1 > > By the way, how do you check for const member functions? I don't remember seeing anything about this in the documentation. I would assume something like has_member_function_xxx< Result (T::*)( Arg0, Arg1 ) const > has_member_function_xxx< T const, Result ( Arg0, Arg1 ) > has_member_function_xxx< T const, Result, boost::mpl::vector2< Arg0, Arg1 >
would be most natural...??? - Jeff

On 7/17/2011 7:02 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 3:18 PM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/17/2011 4:15 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 12:35 PM, Edward Diener<eldiener@tropicsoft.com
wrote:
On 7/17/2011 2:22 PM, Jeffrey Lee Hellrung, Jr. wrote:
Looks like it would be a simple matter of dispatching on
is_member_function_pointer, is_function, and is_member_object_pointer from Boost.TypeTraits.
It is little more complicated than that. The second parameter ( the first is the enclosing type ) in the non-composite form is the return type. A return type could be a member function pointer itself etc., right ?
I think you can dispatch based on (a) the first template parameter is a member function pointer (or member data pointer for MEMBER_DATA)
This means switching the first two parameters when specifying the composite syntax. I am not comfortable with that only because the library regularizes on the notion that the first template parameter is the enclosing type. Otherwise your idea below will work.
Whoops, I had understood the composite syntax to allow you to do
has_member_function_xxx< Result (T::*)( Arg0, Arg1 )>
For BOOST_TTI_HAS_COMP_MEMBER_FUNCTION(xxx) it generates a metafunction called has_comp_member_function_xxx ( in the reference I am giving the wrong name, which I will correct, but the macro metafunction table is correct ) and you are right that the template takes a single parameter of the form 'Result (T::*)( Arg0, Arg1 )' I really was asleep when answering you. This is the one case where the first template parameter is not the enclosing type. So your suggestion of checking the first type for a member function pointer does work if I combine BOOST_TTI_HAS_COMP_MEMBER_FUNCTION and BOOST_TTI_HAS_MEMBER_FUNCTION.
but I see from the documentation of HAS_COMP_MEMBER_FUNCTION that it isn't the case. So why not support the above syntax instead? I don't see the point in specifying the enclosing class type twice. To reiterate what I had above, I propose the following to all be equivalent:
has_member_function_xxx< Result (T::*)( Arg0, Arg1 )> has_member_function_xxx< T, Result ( Arg0, Arg1 )> // my personal preference, probably has_member_function_xxx< T, Result, boost::mpl::vector2< Arg0, Arg1> >
First and third are fine. It is what now exists for BOOST_TTI_HAS_COMP_MEMBER_FUNCTION and BOOST_TTI_HAS_MEMBER_FUNCTION respectively. The second, even if you like it, is unnecessary, and I also use it for BOOST_TTI_HAS_COMP_STATIC_MEMBER_FUNCTION so I do not want to create confusion.
By the way, how do you check for const member functions? I don't remember seeing anything about this in the documentation. I would assume something like
has_member_function_xxx< Result (T::*)( Arg0, Arg1 ) const> has_member_function_xxx< T const, Result ( Arg0, Arg1 )> has_member_function_xxx< T const, Result, boost::mpl::vector2< Arg0, Arg1>
For the first, BOOST_TTI_HAS_COMP_MEMBER_FUNCTION, you have it correct. For the third, BOOST_TTI_HAS_MEMBER_FUNCTION, it would be: has_member_function_xxx< T, Result, boost::mpl::vector2< Arg0, Arg1>, boost::function_types::const_qualified> I could also try to combine BOOST_TTI_HAS_COMP_STATIC_MEMBER_FUNCTION and BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION but that will be a little harder because they both begin with the enclosing type while the former follows the 'Result, boost::mpl::vector2< Arg0, Arg1>' syntax while the latter follows with the 'Result ( Arg0, Arg1 )' syntax.
would be most natural...???
Eddie

On Sun, Jul 17, 2011 at 5:06 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/17/2011 7:02 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
To reiterate what I had
above, I propose the following to all be equivalent:
has_member_function_xxx< Result (T::*)( Arg0, Arg1 )> has_member_function_xxx< T, Result ( Arg0, Arg1 )> // my personal preference, probably has_member_function_xxx< T, Result, boost::mpl::vector2< Arg0, Arg1> >
First and third are fine. It is what now exists for BOOST_TTI_HAS_COMP_MEMBER_**FUNCTION and BOOST_TTI_HAS_MEMBER_FUNCTION respectively.
The second, even if you like it, is unnecessary, and I also use it for BOOST_TTI_HAS_COMP_STATIC_**MEMBER_FUNCTION so I do not want to create confusion.
No, you're right, it isn't necessary. Let me point out that, strictly speaking, neither is the third, as if you want to use has_member_function_xxx in a Boost.MPL placeholder expression, I think boost::function_types::member_function_pointer [1] gives you what you need. Regarding the confusion with the syntax for querying static member functions...can you elaborate on what, exactly, would be confusing? [Also, let me point out, I *myself* have used the "I think it is unnecessary" argument to argue for the *removal* of certain constructs in TTI, so...touche!] I'm assuming the syntax is still fair game for discussion, so, if not, I apologize. But if so, please bear with me, and let's actually look at all of our alternatives. I see 4 potential syntaxes here (let's start with non-const member functions): has_member_function< R (T::*)( U0, U1 ) > // A has_member_function< T, R ( U0, U1 ) > // B has_member_function< T, R, vector2< U0, U1 >, Tag > // C (Tag optional) has_member_function< member_function_pointer< vector4< R, T, U0, U1 >, Tag
::type > // D (Tag optional)
B, to me, would usually be the most convenient, as it has almost no syntactic baggage. Additionally, it still allows the T parameter to be a Boost.MPL placeholder expression, which is likely the most common parameter for a placeholder expression (I'm guessing). So I think B should be included. The only advantage A seems to offer over B is it would be necessary to allow D. Is there some meta-information (e.g., calling conventions) that can be added to member function pointers but not function types...? If so, I guess that alone might necessitate the inclusion of A, assuming it's more convenient to specify that using the Boost.FunctionTypes Tag parameter. I presume the original motivation for including C was to allow various parts of the query to be Boost.MPL placeholder expressions. D does allow this and requires no additional syntax to be directly supported by has_member_function other than A, but maybe it's too verbose. If you think D is too verbose, then inclusion of C is justified. BTW, if you do has_member_function< T, R, Tag >, is it automatically interpreted as has_member_function< T, R, vector0<>, Tag >, or does it error because it tries to use Tag as a Boost.MPL sequence? I don't know if you thought about this, so I thought I should bring it up. FWIW, I would prefer forcing the user to be more explicit, i.e., force the user to explicitly including the vector0<> or empty_sequence type to indicate an empty argument list. By the way, how do you check for const member functions? I don't remember
seeing anything about this in the documentation. I would assume something like
has_member_function_xxx< Result (T::*)( Arg0, Arg1 ) const> has_member_function_xxx< T const, Result ( Arg0, Arg1 )> has_member_function_xxx< T const, Result, boost::mpl::vector2< Arg0, Arg1>
For the first, BOOST_TTI_HAS_COMP_MEMBER_**FUNCTION, you have it correct. For the third, BOOST_TTI_HAS_MEMBER_FUNCTION, it would be:
has_member_function_xxx< T, Result, boost::mpl::vector2< Arg0, Arg1>, boost::function_types::const_**qualified>
Yuck. Would you consider using the cv-qualification of T to determine the cv-qualification of the member function to query?
I could also try to combine BOOST_TTI_HAS_COMP_STATIC_**MEMBER_FUNCTION and BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION but that will be a little harder because they both begin with the enclosing type while the former follows the 'Result, boost::mpl::vector2< Arg0, Arg1>' syntax while the latter follows with the 'Result ( Arg0, Arg1 )' syntax.
Well, Result can never be a function type, so...I think you're good :) - Jeff [1] http://www.boost.org/doc/libs/1_47_0/libs/function_types/doc/html/boost_func...

On 7/17/2011 10:49 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 5:06 PM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/17/2011 7:02 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
To reiterate what I had
above, I propose the following to all be equivalent:
has_member_function_xxx< Result (T::*)( Arg0, Arg1 )> has_member_function_xxx< T, Result ( Arg0, Arg1 )> // my personal preference, probably has_member_function_xxx< T, Result, boost::mpl::vector2< Arg0, Arg1> >
First and third are fine. It is what now exists for BOOST_TTI_HAS_COMP_MEMBER_**FUNCTION and BOOST_TTI_HAS_MEMBER_FUNCTION respectively.
The second, even if you like it, is unnecessary, and I also use it for BOOST_TTI_HAS_COMP_STATIC_**MEMBER_FUNCTION so I do not want to create confusion.
No, you're right, it isn't necessary. Let me point out that, strictly speaking, neither is the third, as if you want to use has_member_function_xxx in a Boost.MPL placeholder expression, I think boost::function_types::member_function_pointer [1] gives you what you need. Regarding the confusion with the syntax for querying static member functions...can you elaborate on what, exactly, would be confusing?
I meant I wanted to have a different composite syntax for member functions and static member functions.
[Also, let me point out, I *myself* have used the "I think it is unnecessary" argument to argue for the *removal* of certain constructs in TTI, so...touche!]
I'm assuming the syntax is still fair game for discussion, so, if not, I apologize. But if so, please bear with me, and let's actually look at all of our alternatives. I see 4 potential syntaxes here (let's start with non-const member functions):
has_member_function< R (T::*)( U0, U1 )> // A has_member_function< T, R ( U0, U1 )> // B has_member_function< T, R, vector2< U0, U1>, Tag> // C (Tag optional) has_member_function< member_function_pointer< vector4< R, T, U0, U1>, Tag
::type> // D (Tag optional)
B, to me, would usually be the most convenient, as it has almost no syntactic baggage. Additionally, it still allows the T parameter to be a Boost.MPL placeholder expression, which is likely the most common parameter for a placeholder expression (I'm guessing). So I think B should be included.
B is still a composite syntax because the function signature is a composite type. In that case I prefer A and will use it, because A mimics the "pointer to member function of class T" which C++ programmers are used to. C is the individual types syntax. I prefer it to D because the enclosing type comes first and it is easier for the end-user. I am only looking to implement a single composite syntax and a single individual types syntax.
The only advantage A seems to offer over B is it would be necessary to allow D. Is there some meta-information (e.g., calling conventions) that can be added to member function pointers but not function types...? If so, I guess that alone might necessitate the inclusion of A, assuming it's more convenient to specify that using the Boost.FunctionTypes Tag parameter.
I presume the original motivation for including C was to allow various parts of the query to be Boost.MPL placeholder expressions. D does allow this and requires no additional syntax to be directly supported by has_member_function other than A, but maybe it's too verbose. If you think D is too verbose, then inclusion of C is justified.
BTW, if you do has_member_function< T, R, Tag>, is it automatically interpreted as has_member_function< T, R, vector0<>, Tag>, or does it error because it tries to use Tag as a Boost.MPL sequence?
It is an error. You need to supply an empty forward sequence if you supply a tag, but if you do not supply a tag the default for the 3rd template parameter is an empty vector so it need not be supplied.
I don't know if you thought about this, so I thought I should bring it up. FWIW, I would prefer forcing the user to be more explicit, i.e., force the user to explicitly including the vector0<> or empty_sequence type to indicate an empty argument list.
By the way, how do you check for const member functions? I don't remember
seeing anything about this in the documentation. I would assume something like
has_member_function_xxx< Result (T::*)( Arg0, Arg1 ) const> has_member_function_xxx< T const, Result ( Arg0, Arg1 )> has_member_function_xxx< T const, Result, boost::mpl::vector2< Arg0, Arg1>
For the first, BOOST_TTI_HAS_COMP_MEMBER_**FUNCTION, you have it correct. For the third, BOOST_TTI_HAS_MEMBER_FUNCTION, it would be:
has_member_function_xxx< T, Result, boost::mpl::vector2< Arg0, Arg1>, boost::function_types::const_**qualified>
Yuck. Would you consider using the cv-qualification of T to determine the cv-qualification of the member function to query?
Yes I should do that. The constness of T should affect what can be found. Good point !
I could also try to combine BOOST_TTI_HAS_COMP_STATIC_**MEMBER_FUNCTION and BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION but that will be a little harder because they both begin with the enclosing type while the former follows the 'Result, boost::mpl::vector2< Arg0, Arg1>' syntax while the latter follows with the 'Result ( Arg0, Arg1 )' syntax.
Well, Result can never be a function type, so...I think you're good :)
You are correct ! Eddie

On Sun, Jul 17, 2011 at 3:18 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/17/2011 4:15 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 12:35 PM, Edward Diener<eldiener@tropicsoft.com**
wrote:
On 7/17/2011 2:22 PM, Jeffrey Lee Hellrung, Jr. wrote:
Well, if the naming scheme is simple (I think it is), I don't think we
have to worry about the metafunction provider nor the metafunction user not understanding the naming scheme. Have users been requesting these GEN macros? Are the GEN macros targeted toward the metafunction provider (who should know what identifiers he's injecting into the namespace) or the metafunction user (who will need to read documentation either way to get the name of the metafunction)? I don't really see the point, and I now refer you to my comment about "less is more" at the top.
The GEN macros are targeted at the metafunction user. If people really feel it is easier reading the documentation and looking at the way that metafunction names are generated than to just repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name, I may remove the GEN macros. I still think that using the equivalent GEN macro for each metafunction macro is a nice and easy way to get the refer to the name of the metafunction for each metafunction macro.
They'd have to read the documentation to figure out how to use the GEN macros, too :) Also "reading the documentation and looking at the way that metafunction names are generated" is 13 words worth work, while "repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name" is 20 words worth work, so the former is by far easier than the latter :)
I thought it would be easier, given let's say macro
BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION(name),
to refer to the metafunction name as
BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION_GEN(name)
than to remember it is:
'has_static_member_function_**name'.
I admit I can be wrong about this but I really, really do not see what harm those GEN macros can cause for those who want to use them instead.
If the GEN macros are primarily for the metafunction *users*, you're really taking up the burden that should be carried by the metafunction *providers*. It would be the metafunction provider's job to ensure their users know their API. The metafunction user may not even know or care about the existence of the TTI library (they should, of course, but I think it's a reasonable possibility). And, sorry, I don't see how one is easier to remember than the other :/ Just take the part of the macro after BOOST_TTI_, lowercase it, and append _'name'. I mean, I thought one of the points about the automatic name generation is so people *don't* have to think about the name of the metafunction... You're giving users 2 different ways to do the same thing. One way you don't really have any choice to give (the user can just use the name of the metafunction directly), and I just don't see the other (the GEN macros) offering any compelling advantages. I think this is laying the groundwork for confusion. - Jeff

On 7/18/2011 1:34 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 3:18 PM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/17/2011 4:15 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Sun, Jul 17, 2011 at 12:35 PM, Edward Diener<eldiener@tropicsoft.com**
wrote:
On 7/17/2011 2:22 PM, Jeffrey Lee Hellrung, Jr. wrote:
Well, if the naming scheme is simple (I think it is), I don't think we
have to worry about the metafunction provider nor the metafunction user not understanding the naming scheme. Have users been requesting these GEN macros? Are the GEN macros targeted toward the metafunction provider (who should know what identifiers he's injecting into the namespace) or the metafunction user (who will need to read documentation either way to get the name of the metafunction)? I don't really see the point, and I now refer you to my comment about "less is more" at the top.
The GEN macros are targeted at the metafunction user. If people really feel it is easier reading the documentation and looking at the way that metafunction names are generated than to just repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name, I may remove the GEN macros. I still think that using the equivalent GEN macro for each metafunction macro is a nice and easy way to get the refer to the name of the metafunction for each metafunction macro.
They'd have to read the documentation to figure out how to use the GEN macros, too :) Also "reading the documentation and looking at the way that metafunction names are generated" is 13 words worth work, while "repeat the metafunction macro, add _GEN to the end, and pass it element name to automatically generate the metafunction name" is 20 words worth work, so the former is by far easier than the latter :)
I thought it would be easier, given let's say macro
BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION(name),
to refer to the metafunction name as
BOOST_TTI_HAS_STATIC_MEMBER_**FUNCTION_GEN(name)
than to remember it is:
'has_static_member_function_**name'.
I admit I can be wrong about this but I really, really do not see what harm those GEN macros can cause for those who want to use them instead.
If the GEN macros are primarily for the metafunction *users*, you're really taking up the burden that should be carried by the metafunction *providers*.
By metafunction users I mean programmers using TTI in their metaprogramming along with MPL, type traits, and such.
It would be the metafunction provider's job to ensure their users know their API. The metafunction user may not even know or care about the existence of the TTI library (they should, of course, but I think it's a reasonable possibility).
And, sorry, I don't see how one is easier to remember than the other :/ Just take the part of the macro after BOOST_TTI_, lowercase it, and append _'name'.
Actually in one case this is currently not followed ( he looks up aghast <g> ). The current BOOST_TTI_VM_HAS_TEMPLATE_CHECK_PARAMS generates the same metafunction name as BOOST_TTI_HAS_TEMPLATE_CHECK_PARAMS, which is 'has_template_check_params_' instead of 'vm_has_template_check_params_'. My reasoning for this was that the non-variadic macro and variadic macro version did the same thing. But this anomaly will go away with the new combined BOOST_TTI_TEMPLATE macro.
I mean, I thought one of the points about the automatic name generation is so people *don't* have to think about the name of the metafunction...
True.
You're giving users 2 different ways to do the same thing. One way you don't really have any choice to give (the user can just use the name of the metafunction directly), and I just don't see the other (the GEN macros) offering any compelling advantages. I think this is laying the groundwork for confusion.
I acknowledge I provide two ways, but I do not think confusion is involved. Even the process of taking a macro name, dropping BOOST_TTI_, lowercasing the rest, and adding _name may seem laborious to some to remember. Can't you conceive that it might be easier for the end-user to use the equivalent _GEN macro. I found it easier when creating the examples so surely there are others who might like that idea.

On Mon, Jul 18, 2011 at 4:49 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/18/2011 1:34 PM, Jeffrey Lee Hellrung, Jr. wrote:
[...]
You're giving users 2 different ways to do the same thing. One way you
don't really have any choice to give (the user can just use the name of the metafunction directly), and I just don't see the other (the GEN macros) offering any compelling advantages. I think this is laying the groundwork for confusion.
I acknowledge I provide two ways, but I do not think confusion is involved.
Well, *I* was confused what their purpose was on my pass through the documentation, and was left wondering if I was missing some important capability of the library :/ - Jeff

Edward Diener-3 wrote:
On 7/16/2011 10:58 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Thu, Jul 7, 2011 at 5:15 AM, Joel Falcou<joel.falcou@gmail.com> wrote:
* Contrary to others opinions, I think Edward should keep both BOOST_TTI_MACRO and BOOST_TTI_TRAIT_MACRO macros (optionally renaming them, if desired; I think these are just following the Boost.MPL naming scheme, but perhaps we can think of names that evoke the difference a little more clearly). It imparts a de facto naming convention for these metafunctions. *But* you should include a warning about the possibility to automatically generate names with double underscores, and that this behavior is undefined.
I will definitely do that. BTW I thought that the double underscores were only considered reserved for the compiler when at the beginning or end of an identifier, but it seems like I am wrong ( haven't found the appropriate comment in the C++ standard ).
A quick search into the ISO C++ standard document for "double underscore" shows: `` 17.4.3.1.2 Global names [lib.global.names] Certain sets of names and function signatures are always reserved to the implementation: - Each name that contains a double underscore (__) or begins with an underscore followed by an upper-case letter (2.11) is reserved to the implementation for any use. - Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace. '' HTH, --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/TTI-Review-for-The-Type-Traits-Introspect... Sent from the Boost - Dev mailing list archive at Nabble.com.

On 7/18/2011 11:16 AM, lcaminiti wrote:
Edward Diener-3 wrote:
On 7/16/2011 10:58 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Thu, Jul 7, 2011 at 5:15 AM, Joel Falcou<joel.falcou@gmail.com> wrote:
* Contrary to others opinions, I think Edward should keep both BOOST_TTI_MACRO and BOOST_TTI_TRAIT_MACRO macros (optionally renaming them, if desired; I think these are just following the Boost.MPL naming scheme, but perhaps we can think of names that evoke the difference a little more clearly). It imparts a de facto naming convention for these metafunctions. *But* you should include a warning about the possibility to automatically generate names with double underscores, and that this behavior is undefined.
I will definitely do that. BTW I thought that the double underscores were only considered reserved for the compiler when at the beginning or end of an identifier, but it seems like I am wrong ( haven't found the appropriate comment in the C++ standard ).
A quick search into the ISO C++ standard document for "double underscore" shows:
`` 17.4.3.1.2 Global names [lib.global.names]
Certain sets of names and function signatures are always reserved to the implementation:
- Each name that contains a double underscore (__) or begins with an underscore followed by an upper-case letter (2.11) is reserved to the implementation for any use.
- Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
Got it. Thanks ! Eddie

The TTI library, which is an abbreviation for the 'Type Traits Introspection' library, allows a programmer to introspect at compile time the inner elements of a C++ type.
The TTI library is based on the type_traits_ext portion of the Concept Traits Library, with improvements and additions, and also reproduces functionality ( without changing existing code ), for the purposes of completeness, from Boost.MPL regarding introspection of types and templates.
Would it make sense to have it part of the actual type trait library instead of as a separate library? Frédéric
participants (6)
-
Edward Diener
-
Frédéric Bron
-
Jeffrey Lee Hellrung, Jr.
-
Joel falcou
-
Joel Falcou
-
lcaminiti