Boost logo

Boost :

Subject: Re: [boost] [TTI] Review
From: Edward Diener (eldiener_at_[hidden])
Date: 2011-07-17 10:05:07


On 7/16/2011 3:02 PM, Lorenzo Caminiti wrote:
> On Sun, Jul 10, 2011 at 11:59 PM, Edward Diener<eldiener_at_[hidden]> wrote:
>> On 7/10/2011 7:59 PM, Lorenzo Caminiti wrote:
>>>
>>> Review of Boost.TTI 2011-07-10 (Lorenzo Caminiti)
>>> =================================================
>>>
>>>
>>> Do you think the library should be accepted as a Boost library?
>>> ---------------------------------------------------------------
>>>
>>> Yes, in my opinion Boost.TTI should be accepted as a Boost library.
>>
>> Appreciated !
>
> Thank you for programming libraries that make my programming easier
> (like TTI and VMD).

Your welcome.

>
> As I said, I am going to reply to all your comments here one by one.
>
>>> Key points are:
>>>
>>> 1) I would essentially found this library useful as is.
>>>
>>> 2) The library should not expand its macros into the boost::tti
>>> namespace (I give a few reasons and suggest alternatives below).
>>>
>>> 3) The library uses too many macros to provide essentially the same
>>> functionality (with minor variations). This is confusing and the
>>> library macro API should be simplified (I suggest a possible
>>> simplification below).
>>
>> I will answer each of your points below rather than generally answering 2)
>> or 3) above.
>>
>>>
>>>
>>> My comments are all numbered and marked as follow:
>>> [MUST] If these comments are not addressed, I would no longer
>>> recommend to add this library into Boost.
>>> [WANT] I would like to see these comments addressed but I would still
>>> recommend to add this library into Boost even if these comments are
>>> not addressed.
>>> [NOTE] I do not feel strongly about these comments and the authors can
>>> ignore these comments if they wish to do so.
>>>
>>>
>>> What is your evaluation of the design?
>>> --------------------------------------
>>>
>>> 1. [MUST] The library provides too many macros (with very similar
>>> functionality) which make the interface difficult to grasp.
>>> The authors should take some time to try to redesign and simply the
>>> library interface ideally reducing the number of macros.
>>>
>>
>> I will give my reasons for each of the macros in answer to your suggestions
>> below, but I completely agree that if the number of macros could be
>> simplified and still offer the same basic functionality it should be done.
>>
>>> [WANT] I would reduce the macro API to the following 5 macros (no
>>> more, as I explain in the rest of the comments below):
>>>
>>> HAS_TYPE(has_mytype, mytype)
>>> HAS_TEMPLATE(has_mytpl, [template(...) {class|struct}] mytpl)
>>
>> I can look into combining the various template macros if it can be done.
>> Currently there are three variations, disregarding the complex form of each
>> macro. The variations are:
>>
>> BOOST_TTI_HAS_TEMPLATE(name)
>> BOOST_TTI_HAS_TEMPLATE_CHECK_PARAMS(name,pp-seq-of params)
>> BOOST_TTI_VM_HAS_TEMPLATE_CHECK_PARAMS(name,variadic-sequence-of-params)
>>
>> You would like to see a single macro, called BOOST_TTI_HAS_TEMPLATE, which
>> could alternately take a pp-seq-of params or a variadic-sequence-of-params.
>> I agree that would be wonderful if it could be done.
>>
>> To do this the compiler must support variadic macros AFAICS. What if the
>
> No, if the compiler does not support variadics then the macro will
> only accept the pp-sequence. If the compiler supports variadics then
> the macro will accepts both the pp-sequence and the variadic pp-tuple.
> It should be possible to program this using pp techniques similar to
> the ones I have used to program BOOST_LOCAL_FUNCTION_PARAMS (see
> http://svn.boost.org/svn/boost/sandbox/local/boost/local/function.hpp).
>
> That clarified, I think we discussed this point enough in the other
> replies to this email thread. At the end I will use any API you decide
> to provide so case closed :)
>
>> end-user's compiler does not do so ? Even if the end-user's compiler
>> supports variadic macros, how do I tell the difference between a pp-seq of
>> params and a variadic-sequence of params ?
>>
>> So here are my thoughts. If I supported the single macro for compilers which
>> support variadic macros, I still need to support the two non-variadic macro
>> versions for compilers which do not support variadic macros.
>>
>> For the variadic macro version I can find out that is the
>> variadic-sequence-of-params if it has more than one variadic parameter after
>> the name. If it only has one variadic parameter after the name I can check
>> if the parameter starts with a set of parens and, if it does, it is the
>> pp-seq of params, else it is the variadic-sequence of params. I think this
>> is doable. But I still can not see my way around dropping support completely
>> for the non-variadic versions of this macro.
>>
>>> HAS_MEMBER_VARIABLE(has_myvar, [static] myvar)
>>> HAS_MEMBER_FUNCTION(has_myfunc, [static] myfunc)
>>
>> I can easily combine BOOST_TTI_HAS_MEMBER_DATA and
>> BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION into HAS_MEMBER_VARIABLE, which covers
>> either case, and BOOST_TTI_HAS_MEMBER_FUNCTION and
>> BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION into a HAS_MEMBER_FUNCTION, which cover
>> either case. But then the end-user will lose the ability to distinguish
>> between a 'member data/static member data' or 'member function/static member
>> function'. Do you really think that is the correct thing to do just because
>> you think there are too many macros ? I do not.
>>
>> OTOH I do not mind adding the combined member data and member function
>> macros as you suggested, while keeping what already exists, but then that
>> adds more macros ( which does not bother me a bit ), which you do not like.
>>
>> See below for my comments about the composite type functions.
>>
>>> MEMBER_TYPE(trait, name)
>>>
>>> 2. [MUST] Expanding the metafunctions within the boost::tti namespace
>>> presents a number of serious issues:
>>> a) If multiple part of the program (possibly independent libraries)
>>> use the TTI library to introspect the same name, the metafunction
>>> names will clash.
>>> b) The library macros can only be used at namespace scope. However, it
>>> should be possible to use the macros also at class scope so to define
>>> the introspection metafunctions as inner classes, etc (this is
>>> critical for example in my application for Boost.TTI where I use it to
>>> implement Boost.Contract and all Boost.Contract macros expand at class
>>> scope).
>
> BTW, the docs should indicate within which scope the macros can be
> used. I think the macros should be used within namespace and
> class/struct scope. Unfortunately, I don't think there is a way to use
> the macros within local scope (e.g., within a function) because local
> classes cannot be templates. This should be documented.

The macros generate metafunctions, which are templates of a particular
syntax ( nested 'type' member ). So wherever a template can occur is
valid. They are no different from any other metafunction as determined
by boost MPL.

I really do not see the necessity of having to say much more, but I will
put in a note to say the above. I will also mention, because of ODR,
that the macros should be put within a namespace at the minimum and/or
the traits parameter can be used to give the metafunction a distinct
name within whatever scope the macro is used.

>
>>> The authors should address these issues.
>>
>> I totally agree with your criticism here and I will remove the generated
>> macros from any namespace. Others have also mentioned the same thing and I
>> immediately realized they were right when I read their reasoning.
>>
>>>
>>> [WANT] To address these issues, I would suggest to simply define the
>>> metafunctions within the enclosing scope. It will be the user's
>>> responsibility to use the macros within the correct scope (namespace,
>>> class, etc). For example:
>>>
>>> template< class T>
>>> struct OurTemplateClass {
>>> BOOST_TTI_HAS_TYPE(has_mytype, _mytype) // also at class scope
>>>
>>> void f() {
>>> std::cout<< has_mytype<T>::value<< std::endl;
>>> }
>>> };
>>>
>>> This should also allow to remove all the GEN/GEN_BASE macros.
>>
>> I can remove the GEN_BASE set but I would still keep the GEN set ( which
>> would be the same as the current GEN_BASE set ) in order to provide an easy
>> way to generate the metafunction name from the appropriate macro, without
>> the end-user having to manually remember the algorithm by which I generate
>> the metafunction name.
>>
>> See below for my comments about generating the metafunction name.
>>
>>>
>>> 3. [MUST] The library macros prefix the specified name with _ but
>>> this can create a symbol with double underscores __ which is reserved
>>> by C++ (e.g., HAS_TYPE(_my_type) creates a metafunction named
>>> boost::tti::has_type__my_type which is a reserved symbol).
>>> The authors should address this issue.
>>
>> You have made a good point and I can simply remove the preceding '_' when
>> generating the final metafunction name.
>
> But then HAS_TYPE(mytype) generates the unreadable name has_typemytype
> :( Unfortunately, as a user of your library I won't think this is a
> good solution...

I am tempted to keep the name generation I already have, with the
leading _, and tell the end-user that if the 'name' being introspected
begins with an underscore ( _ ), then the traits form of each macro
should be used to avoid a generated name with a double underscore ( __ ).

The end-user should use the equivalent GEN macro to generate the
resultant macro metafunction name rather than having to remember the
naming convention I finally decide on. That is why the equivalent GEN
macros were created.

>
>>> [WANT] To address this issue (and also to reduce the number of macros
>>> eliminating the need for separate TRAIT macros), I would suggest to
>>> always ask the user to specify both the trait and the introspected
>>> name (not just the introspected name) and then use the trait to name
>>> the metafunction (as always, it is the user's responsibility to not
>>> use reserved names). For example:
>>>
>>> HAS_TYPE(has_mytype, _mytype) // generates metafunc has_my_type
>>>
>>> This should also allow to remove all the TRAIT macros.
>>
>> You want to remove functionality for automatically generating a macro
>> metafunction name just because you feel there are too many macros. I believe
>
> No, it's for two reasons (1) the generated name can contain __ and (2)
> there are too many macros (not just (2)). As I said, if you address
> (1) by removing the _ prefix then the names will be unreadable:
>
> HAS_TYPE(mytype) // generates has_typemytype
>
> I think the name has_typemytype is unreadable. I will then always use
> the TRAIT macros to generate more readable names like has_type_mytype
> and making the automatic name generation functionality useless.
> However, if you can fully address (1) then (2) might not be a strong
> enough reason to remove the automatic name generation but how can you
> really address (1) in general?
>
> Just to shared some ideas, I tough about the possibility to generate a
> name like has_type::mytype. For example, using an enclosing struct:
>
> struct has_type {
> template< typename T> class mytype { ... }; // metafunc from your macro
> };
>
> But then you cannot expand two macros HAS_TYPE(mytype) and
> HAS_TYPE(yourtype) within the same context because the enclosing
> struct name has_type will clash so I concluded this a very bad idea.
> Also you can't use a namespace has_type because of all the namespace
> issues we already discussed... too bad because if there was a way to
> automatically generate a name like has_type::mytype I think the
> automatic name generation functionality would be cool.
>

I do not want to get into enclosing structs, especially as the problem
of an enclosing namespace has been pointed out to me.

>> that the automatic generation of the metafunction name is welcomed by
>> metaprogrammers. The MPL macros
>> BOOST_MPL_HAS_XXX_TEMPLATE_DEF/BOOST_MPL_HAS_XXX_TEMPLATE_NAMED_DEF and
>> BOOST_MPL_HAS_XXX_TRAIT_DEF/BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF follow the
>> scheme I have chosen.
>>
>>>
>>> 4. [WANT] I really think that mixing () and<> parenthesis is confusing:
>>>
>>> HAS_TEMPLATE_CHECK_PARAMS(MoreParameters,
>>> (class) (class) (int) (short) (class)
>>> (template<class)(int> class InnerTemplate) // confusing :(
>>> (class)
>>> )
>>>
>>> IMO, this would be more readable as the following:
>>>
>>> HAS_TEMPLATE_CHECK_PARAMS(MoreParameters,
>>> (class) (class) (int) (short) (class)
>>> (template( (class) (int) ) class InnerTemplate) // ok :)
>>> (class)
>>> )
>>
>> I will look into this. It is obviously more difficult programming with the
>> latter than the former, but it is probably doable although with much effort.
>> It may not be worth the effort.
>>
>> What the former syntax reflects is an exact transcription of the template
>> parameters with each comma replaced by ')(' and a starting and ending
>> parentheses. So for the template:
>>
>> 'template<class,class,int,class,template<class> class
>> InnerTemplate,class,long> struct ManyParameters { }'
>>
>> the parameters are:
>>
>> '(class)(class)(int)(class)(template<class> class
>> InnerTemplate)(class)(long)'
>>
>> and all I had to do as an end-user was copy the template parameters, add a
>> '(' at the beginning, add a ')' at the end, and change every ',' to a ')('.
>> As a programmer I take the sequence and directly convert it to the template
>> parameters via a BOOST_PP_SEQ_ENUM.
>
> I think we discussed this well enough in previous replies to this
> email thread. Please try to think first of the syntax that your
> library users (your customers) will find most useful and don't think
> about how complex it will be to pp-program within your library. Again,
> at the end I will use any syntax you decide to provide.
>
>>> 5. [NOTE] There is no real need to use another macro ..._CHECK_PARAMS
>>> because HAS_TEMPLATE can be reused. This would reduce the number of
>>> macros. For example, with template parameters:
>>>
>>> HAS_TEMPLATE(
>>> template( // gives the parameters (optional)
>>> (class) (class) (int) (short) (class)
>>> (template( (class) (int) ) class InnerTemplate)
>>> (class)
>>> )
>>> struct MoreParameters // struct or class can be used here
>>> )
>>>
>>> And the same macro without template parameters:
>>>
>>> HAS_TEMPLATE(MoreParameters)
>>
>> See above for my answer to this.
>>
>>>
>>> 6. [NOTE] The same macros can accept both pp-sequences and variadic
>>> pp-tuples (when variadic macros are supported). This eliminates the VM
>>> macros reducing the number of different macros in the library API. For
>>> example:
>>>
>>> HAS_TEMPLATE(
>>> template(
>>> class, class, int, short, class,
>>> template( class, int ) class InnerTemplate, // variadic commas
>>> class
>>> )
>>> struct MoreParameters
>>> )
>>>
>>> I know this uses template( class, int ) instead of template< class,
>>> int> but this way the syntaxes with and without variadics are unified
>>> and consistent (and more readable IMO). Alternatively, you can
>>> probably program the macro with variadic to be:
>>>
>>> HAS_TEMPLATE(
>>> template< // template< > here
>>> class, class, int, short, class,
>>> template< class, int> class InnerTemplate, // template< >
>>> here
>>> class
>>> >
>>> struct MoreParameters
>>> )
>>>
>>> But still keep the following syntax without variadics:
>>>
>>> HAS_TEMPLATE(
>>> template( // template( ) here
>>> (class) (class) (int) (short) (class)
>>> (template( (class) (int) ) class InnerTemplate) // template( )
>>> here
>>> (class)
>>> )
>>> struct MoreParameters
>>> )
>>
>> See above for my answer to this.
>>
>>>
>>> 7. [WANT] Can the authors explain why the inner template parameter
>>> name InnerTemplate is needed while all other template parameter names
>>> are not needed? Is it really needed? Why I cannot do:
>>>
>>> HAS_TEMPLATE(
>>> template(
>>> class, class, int, short, class,
>>> template( class, int ) class, // no name InnerTemplate
>>> class
>>> )
>>> struct MoreParameters
>>> )
>>
>> It should not be needed. Please try without it. If it does not work I will
>> look and see why and attempt to correct it.
>
> I didn't have time to try. However, can you please verify this and
> remove the InnerTemplate parameter name from the docs? (Or put a note
> in the docs that say why this parameter name is needed.) I got
> confused because the docs name this one parameter but not the other
> parameters...
>

I have verified it and it works without the inner template parameter
name. One can include the inner template parameter in the template or
not, one can include the inner template name in the macro or not, and
the introspection works without problems.

I will remove that inner template name in the examples in the docs and
in my tests.

>>> 8. [NOTE] There is no need to use a different set of macros for
>>> static because the keyword static can be used to have the pp detect
>>> this case. This will reduce the number of different macros. For
>>> example:
>>>
>>> HAS_STATIC_MEMBER_FUNCTION(has_static_f, f)
>>>
>>> Can be replaced by:
>>>
>>> HAS_MEMBER_FUNCTION(has_static_f, static f)
>>
>> See above for my previous discussion on dropping the distinction between
>> member functions and static member functions.
>
> We discussed this well enough in other replies to this email thread. I
> totally respect your decision that [1] is more clear than [2]:
>
> HAS_STATIC_MEMBER_FUNCTION(func) // [1]
> HAS_MEMBER_FUNCTION(static func) // [2]
>
> However, I would like to offer the reflection that [2] is a more
> flexible API than [1]. Say your library is expanded to detect public,
> protected, and private and you can do that for both static and
> non-static member functions. Then you will have to provide lots of
> macros for all these combinations:
>
> HAS_MEMBER_FUNCTION
> HAS_PUBLIC_MEMBER_FUNCTION
> HAS_PROTECTED_MEMBER_FUNCTION
> HAS_PRIVATE_MEMBER_FUNCTION
> HAS_STATIC_MEMBER_FUNCTION
> HAS_STATIC_PUBLIC_MEMBER_FUNCTION
> HAS_STATIC_PROTECTED_MEMBER_FUNCTION
> HAS_STATIC_PRIVATE_MEMBER_FUNCTION
>
> Now say you can also detect virtual public, protected, or private
> member functions, you have to add another 4 macros for that too...
> Instead the other syntax will only and always use 1 macro:
>
> HAS_MEMBER_FUNCTION( [public | protected | private] [static | virtual] func )
>
> So the API [2] will never blowout if more traits can be introspected
> while [1] will blowout fast. That said, this is not a concern for your
> library right now because you can only introspect the one static
> trait.

You have made a good point. I will think about this further.

The current problem is mainly that the macro metafunction generated
depends on the difference between 'static' and 'non-static' member
functions or 'static' and 'non-static' data members. I could generate a
metafunction for both and then use a traits-like template parameter to
distinguish between them, but that would mean some code bloat when
unnecessary. A programmer would be paying for functionality he may not use.

OTOH I do not like the idea of passing such decisions as macro
parameters because that means more complicated macro syntax and I would
like to keep the macros as simple as possible while letting the
metafunctions absorb any complications.

I am much less against additional macro names than you are. I see
nothing wrong with a well-documented and large API, with a programmer
understanding what has to be used for what needed functionality.

I do understand your point about proliferating combinations.

>
>>> 9. [WANT] Why can't we always use the composite function syntax R
>>> (C::*)(A0, A1, ...)? This is similar to the preferred notation for
>>> Boost.Function so if possible it should be used instead of the other
>>> notation.
>>>
>>> As I understand it, some compilers have troubles supporting the
>>> composite syntax. However, these compilers might not be able to
>>> support the TTI library all together. Unless there is evidence of
>>> compilers that can support TTI but not the composite function syntax,
>>> I think only the composite function notation should be provided.
>>>
>>> This should also allow to remove all the COMP macros.
>>
>> The reason for having both a composite syntax, which you like, and the
>> non-composite syntax of individual types is:
>>
>> 1) The composite type syntax can be used when one has to pass a calling
>> convention, or possible some compiler-specified function-like syntax, which
>> the function_types 'tag' type does not support. I think this is absolutely
>> necessary to have.
>>
>> 2) The individual type syntax is there so that MEMBER_TYPE can be used to
>> pass a nested type which does not have to exist without causing a compiler
>> error. I also thin this is absolutely necessary to have.
>>
>> Please read the section called 'Nested Types' in the documentation to
>> understand why MEMBER_TYPE is used. The ability to pass a nested type in the
>> form of a MEMBER_TYPE as a function parameter is very important piece of
>> functionality. Being able to do this for a function parameter type or for
>> the function return type is something I do not want to eliminate.
>
> Thank you for explaining this. I am still thinking about it... Could
> you please reply with an example of a MEMBER_TYPE can is used with the
> individual type syntax but fails if passed to the composite syntax?
> That will help me a lot in fully understanding this issue.

As one typical example, you have a type T and one of the types in a
function signature is a possible nested type of T called NestedType and
you are introspecting for a static function of T whose signature is 'int
SomeStaticFunction(T::NestedType)'.

If you use composite syntax:

BOOST_TTI_HAS_COMP_STATIC_MEMBER_FUNCTION(SomeStaticFunction)
BOOST_TTI_HAS_COMP_STATIC_MEMBER_FUNCTION_GEN(SomeStaticFunction)<T,int
SomeStaticFunction(T::NestedType)>::value

you will get a compiler error if T::NestedType does not exist but if you
use individual type syntax:

BOOST_TTI_MEMBER_TYPE(NestedType)
BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION(SomeStaticFunction)
BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION_GEN(SomeStaticFunction)<T,int,boost::mpl::vector<BOOST_TTI_MEMBER_TYPE_GEN(NestedType)::type><T>
>::value

you will not get a compiler error but the value will be false if
T::NestedType does not exist, which is really what you want. Of course
T::Nested type may exist but the static member function you are
introspecting may not exist, and the value will still be false. But you
will not get a compiler error in either case.

>
>>> 10. [NOTE] I think "member variable" is a more accepted name that
>>> "member data"-- isn't it? If so, I'd rename MEMBER_DATA to
>>> MEMBER_VARIABLE.
>>
>> I can understand your preference. I will consider it.
>>
>>>
>>> 11. [WANT] Is it possible to have some sort of composite syntax for
>>> member variables?
>>
>> There is no reason to have a composite syntax for member variables. A member
>> variable is a single type.
>
> As I said in a previous reply (1) the C++ standard name is data member
> (so HAS_MEMBER_DATA or HAS_DATA_MEMBER are good names as far as I am
> concerned) and (2) there exist a composite data member syntax:
>
> struct c {
> int x;
> };
>
> int c::* // type of pointer to int data member of c
>
> So:
>
> HAS_MEMBER_DATA(x) // [1]
> has_x<c, int> // 2 tparams
>
> HAS_COMP_MEMBER_DATA(x) // [2]
> has_comp_x<int c::*> // 1 (composite) tparam
>
> That said if you think there is no reason to support the composite
> data member syntax [2], that's fine. I'd add a note to the docs
> explaining why the composite syntax is supported for member functions
> but not for data members.

I will add that to the docs.

>
>>> I remember reading something about this for
>>> overloading the operator ->* to access member variables (as well as
>>> calling member functions) but I can't double check right now... can
>>> the authors look into this? For example:
>>>
>>> HAS_MEMBER_VARIABLE(has_number, number)
>>>
>>> has_number<T::short> // composite form for member variable?
>>>
>>> If there exists a composite form for member variables, I would like
>>> the generated metafunctions to use it instead of the plain form (so to
>>> be consistent with the MEMBER_FUNCTION macros-- see comment above).
>>>
>>> 12. [WANT] Are the metafunction MTFC macros really needed? The docs
>>> say they reduce compile-time (compared to using MPL placeholders) but
>>> no evidence is shown to support that...
>>>
>>> Extra macros complicate the library API so if they are provided for
>>> reducing compile-time there should be some benchmarking showing their
>>> benefits. If not, these macros should be removed.
>>>
>>> [NOTE] If these macros are shown to be beneficial, I'd rename them
>>> with a METAFUNC postfix because I find it more readable. For example:
>>>
>>> HAS_TYPE_METAFUNC
>>> HAS_MEMBER_FUNCTION_METAFUNC
>>
>> I supplied them merely as a convenience. In an early mailing list message
>> about the TTI library from Dave Abrahams, before this review, he suggested
>> that passing metafunctions as data as a metafunction class would generally
>> be faster than passing them via placeholder expressions. I do not mind
>> eliminating them if it seen as overkill.
>
> If there is evidence (compile-time data and possibly some explanation)
> of that MTFC have faster compile-time, leave the macros. However, I
> thinks you either (1) measure the compile-times add the evidence in
> the docs or (2) you remove the MTFC macros.

Having the MTFC macros does not hurt anything. There is no reason to not
supply them purely as a convenience.

>
>>> 13. [WANT] Are the nullary metafunctions really needed?
>>
>> Functionally, no, which the documentation explicitly explains. Syntactically
>> I feel they are much easier to use once they are understood.
>>
>>> The docs say
>>> they simplify the syntax but no side-by-side example comparing the
>>> syntax with and without the nullary type metafunctiosn is provided...
>>>
>>> Unless their benefit is clearly shown, the nullary metafunctions
>>> should be remove to simplify the library API.
>>
>> I will add side by side examples showing the simpler syntax of using the
>> nullary metafunctions. I do show the different syntaxes for similar
>> situations in the doc, but not side by side.
>
> Can you please reply to this email with a short example that compares
> the two syntaxes so we can all take a look at it?
>
>> Why not just ignore them if you do not like their syntax ? After all tghey
>
> Because as a new user of your library I think "are they important?",
> "shall I learn them?", "do I need/want their simpler syntax?". So if
> they really offer a benefit (even just syntactical), they should be
> there, just illustrate their benefit better.

There are sections in the doc about "Nullary Type Metafunctions" and
"Using the Nullary Type Metafunctions". I will work on expanding the
documentation in these sections, perhaps adding some more sections, so
that others understand the syntactical advantage of using the nullary
metafunctions if they want to do so.

>
>> are just a set of metafunctions, which no one has to learn to use if they do
>> not want to do so. They do not interfere with anything else in the library
>> and they allow specifying nested types in a syntactically easier way.
>>
>>>
>>> 14. [NOTE] I'd fully spell the header file names to improve
>>> readability and be consistent with the macro names (which are
>>> correctly fully spelled). For example, "member_function.hpp" instead
>>> of "mem_fun.hpp".
>>
>> I can do that, and probably will. You have a good point.
>>
>>>
>>> 15. [NOTE] I'd rather use an actual name for the library instead of
>>> the cryptic acronym TTI (but I personally don't like the widely used
>>> MPL neither so maybe it's just me). Maybe "intro" (for introspection),
>>> or "mirror" (look at yourself), or "soul" (look into your soul), or
>>> "psycho" (look into your person) ;) would be better names...
>>
>> Where would you like to see the longer name ? I certainly do not mind
>> calling the library Type Traits Introspection but that is quite a mouthful.
>> Like MPL ( for Metaprogramming Library ), TTI is easier to remember and
>> refer to.
>>
>>>
>>>
>>> What is your evaluation of the implementation?
>>> ----------------------------------------------
>>>
>>> 16. I did not look at the implementation.
>>>
>>>
>>> What is your evaluation of the documentation?
>>> ---------------------------------------------
>>>
>>> 17. [WANT] I'd add a "Motivating Example" in the Introduction section
>>> to very briefly illustrate the library functionality (probably right
>>> after the bullet list on the library functionalities). I had to go all
>>> the way into the Nested Types section to start seeing some code... (I
>>> was motivated just because I knew what the library does and I've used
>>> MPL_XXX macros with SFINAE before).
>>
>> I totally agree with you. I intend to revamp the introductory material to
>> make it simpler and easier to understand what the library can do, and
>> present some basic motivating examples.
>>
>>>
>>> 18. [NOTE] I don't think you need a space before "?" in English (see
>>> "Why the TTI Library ?", etc).
>>
>> You are right. I will change it. It is just my style but unnecessary.
>>
>>>
>>> 19. [WANT] Typo in "depending on how many parameters are bring passed".
>>
>> Corrected ! Thanks !
>>
>>>
>>>
>>> What is your evaluation of the potential usefulness of the library?
>>> -------------------------------------------------------------------
>>>
>>> 20. [NOTE] The library is very useful as is. For example, I use a
>>> similar introspection technique in Boost.Contract as part of a
>>> mechanism to check if a base class has a virtual function that is
>>> being overridden so to automatically subcontract from such a base
>>> class function.
>>>
>>> 21. [WANT] I think the docs should add an annex with a complete list
>>> of all traits that would ideally be introspected even if they cannot
>>> actually be introspected. For example, a table could list all traits
>>> that would be good to introspect and then for each trait say
>>> "introspected by this library", or "cannot be introspected in C++
>>> because...", etc. For example, Boost.Contract could use the ability to
>>> check if a function is public, protected, or private to simplify its
>>> macro syntax (because only public functions shall check class
>>> invariants).
>>>
>>> Possible traits for introspection I can think of are: is public, is
>>> protected, is private, is template, has these template parameters, is
>>> explicit, is inline, is extern, is static, is virtual, is const
>>> member, is volatile member, has these exception specifications
>>> (throw), any more?
>>
>> I think your possible list is too arbitrary. Most anything can be added
>> here.
>
> No, my list is not arbitrary but it tries to include _all_ the traits
> in a function (free, member, constructor, and destruct) declaration:
>
> func_signature_:
> // for free functions, member functions, constructors, and destructors
> [public | protected | private]
> [[export] [template< template_param, ...>]
> [explicit] [inline] [extern] [static] [virtual]
> [result_type_] func_name_ ( func_params_ ) [const] [volatile]
> [throw( [expections_] )]
>
> Also:
> 1) How about classes? For example, a metafunction to introspect "is B
> a protected base of class C?"
> 2) How about namespaces? For example, a metafunction to introspect "is
> f an free function in namespace n?"
>
> It is true that most anything can be added here. However, the traits
> supported by TTI are actually arbitrary. Why there is a matafunction
> for "is f a static member of class C?" but not for "is f a virtual
> member of class C?". From a library user prospective, I might be
> interested in the static trait as much or as well as in the virtual
> trait. However, TTI now introspects the static trait but not the
> virtual trait and that seems arbitrary for the user. If other traits
> cannot be introspected (like virtual, public, etc), that is fine but
> you should document somewhere that TTI does not introspect them not
> because we "forgot" these other traits but because it is not possible
> to introspect them in C++.
>
> Also this annex should serve as the "total" reference for C++
> introspection. If I want to know if a public member function can be
> introspected in C++, I should be able to look at your library and
> either find the API that introspects the public trait or find that
> such a trait cannot be introspected. I think you need to address all
> the other traits (maybe just saying they cannot be introspected)
> otherwise you library is not a general introspection library but it is
> an introspection library only for the 4 traits it can introspect.
>

The macros provided are not arbitrary. They are a core set of
introspection macros for each type of inner element. That you can dream
up more possibilities is fine, but it is not the responsibility of my
library to tell you about all the possibilities and to then tell you
that the library only does the basic set. Any library design is finite
and chooses what it wants to do. I welcome the knowledge of other
possibilities but that does not mean the library can do them or even try
to do them. At that rate no library will ever be finished.

>>> 22. [WANT] I'd add an annex to the docs to compare this library with
>>> other libraries (e.g., the "mirror" library?) that exist out there for
>>> introspection (i.e., some sort of literature review).
>>
>> if there were an existing Boost library I might do it.
>
> Why would you compare Boost.TTI only with other Boost libraries? You
> should do a literature search on C++ introspection in general and
> compare Boost.TTI with *all* known C++ introspection library
> even/especially if they are not in Boost.

I disagree with you. It is not the responsibility of a library developer
to investigate and document every other possibile similar library. I can
understand that if there were a C++ standard library or a Boost library
which offered similar features to what another Boost library is
attempting to provide, then it would be good for the developer to
compare his library to what is already exists in that domain to
illustrate the advantages and disadvantages of one's own approach.

>
>>> I would also
>>> comment on possible C++ standard extensions or compiler specific
>>> support for introspection.
>>
>> Which C++ standard ( 2003 or C++0x ) ? For the latter I still have much to
>> learn since it is so new.
>
> Both standards 2003 and C++0x. Plus you should look at
> compiler-specific extensions (maybe Clang has some introspection
> feature??). For example, SFINAE *might* allow to detect the private
> trait in C++0x but not in C++03 (I don't really know but a Boost
> introspection library should know and document that).
>
> Again, the idea for this "comparison" annex is to be as complete as
> possible (like a literature search when you write an academic paper).
> This might not change your library functionality at all but it will
> clearly position the features offered by your library with respect to
> what can be done (or will be possible) with C++ introspection and it
> will benefit your library users so they will know if a trait they are
> interested in introspecting can or not be introspected in C++.

I can see this, but I really know little, except for extended SFINAE in
C++0x, which could be mentioned. I will mention that in my docs. if
there are any other features, even compiler specific features, which aid
in the introspection of inner elements, I would be glad to be informed
about them and use them.

A Boost library is not an academic paper, and I have the greatest
respect for academic scholarship. There is just so much information that
the library developer should be expected to present to the end-user.

>
>>> Did you try to use the library? With what compiler? Did you have any
>>> problem?
>>>
>>> -----------------------------------------------------------------------------
>>>
>>> 23. Yes, I compiled all the examples from the docs and played around
>>> with the library a bit more. I have used GCC 4.3.4 on CygWin and I had
>>> no issue.
>>>
>>>
>>> How much effort did you put into your evaluation? A glance? A quick
>>> reading? In-depth study?
>>>
>>> --------------------------------------------------------------------------------------------
>>>
>>> 24. I'd say in between a quick reading and an in-depth study. I spent
>>> about 12 hours total reading the docs, trying the examples, and
>>> writing this review.
>>>
>>>
>>> Are you knowledgeable about the problem domain?
>>> -----------------------------------------------
>>>
>>> 25. I have used SFINAE before to introspect if a class has a given
>>> inner class to implement subcontracting for Boost.Contract.
>>> Furthermore, I have used template metaprogramming in a number of
>>> occasions.
>>>
>>> Overall, I'd say that I am familiar with the problem domain but I am
>>> not an expert.
>>>
>>>
>>> --Lorenzo
>>
>> Thanks very much for your review and specific comments.
>
> Thank you again for putting together useful libraries and keep up the good work!

Like all software developers and designers of any expertise I take great
pride in doing the best work I can.

Eddie


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