Boost logo

Boost :

Subject: Re: [boost] [TTI] Review
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-07-16 15:02:40

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

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

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
> either case, and BOOST_TTI_HAS_MEMBER_FUNCTION and
> 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 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...

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

> that the automatic generation of the metafunction name is welcomed by
> metaprogrammers. The MPL macros
> scheme I have chosen.
>> 4.  [WANT] I really think that mixing () and<>  parenthesis is confusing:
>>        (class) (class) (int) (short) (class)
>>        (template<class)(int>  class InnerTemplate) // confusing :(
>>        (class)
>>    )
>> IMO, this would be more readable as the following:
>>        (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:
>>        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:
>>        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:
>>        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:
>>        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:
>>        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

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


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

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

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


has_x<c, int> // 2 tparams

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

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

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

        // 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_] )]

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.

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

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


Boost list run by bdawes at, gregod at, cpdaniel at, john at