Boost logo

Boost :

From: Tobias Schwinger (tschwinger_at_[hidden])
Date: 2005-06-07 16:21:47


Jody Hagins wrote:
>>Seems like we have a fundamentally different taste when it comes to
>>documentation...
>
>
> Probably.
>
>
>
>>Are you sure you're not overdoing it ?
>
>
> Obviously, I don't think so ;-) However, it's your submission, and you
> have the final say of what you are willing to add. All I can do is
> provide my opinion (which, no matter how many words I write, is still
> ONE opinion -- albeit an important opinion to me).
>
>
>
>>Does perhaps it help to say "infinite sets" ?
>>Or even "infinite immutable sets" ?
>>
>>Every occurence of "set of types" is almost immediately followed by
>>"specified by one of the tag types" or alike, where "tag types" is a
>>hyperlink to the description.
>
>
> To me, a set of types is just the wrong description, as it conveys
> something completely different. If you still want to use that term, I
> think the TAGTYPES section should more explicitly explain this concept.
> Currently, it starts out talking about "kinds" and then later, slips in
> the term "sets" almost as an afterthought, though this is an important
> concept (and it is something that actually cnofused me -- I thought I
> was supposed to combine the tag-types into a set of 1 or more tag-types
> and check if a function belonged to any of those tag-types)
>

OK - this is a concrete hint of what goes wrong and gives me an idea of what to
change.

> After thinking about it a little, maybe a small change would make the
> "set" concept more clear. Currently, you have this sentence:
>
> The Function Types library uses tag types to describe different kinds of
> function types, and arbitrary supersets of these and their inverse.
>
> Maybe changing to to something like:
>
> The Function Types library uses tag types to describe different kinds of
> function types. Each tag type names a particular set of similar
> function types. For example, given these types:
> typedef void (*foo_t)(int);
> typedef int (*bar_t)(std::string const &);
> struct blarg_t { void foo(); };
> struct foobar_t { int bar(double) const; }
> foo_t and bar_t (along with all other non-member/static functions) are
> elements of the set named plain_function. Likewise, &blarg_t::foo and
> &foobar_t::bar (along with all other member function pointers) are
> elements of the set named member_function_pointer. In addition,
> &foobar_t::bar is also a member of the set
> const_member_function_pointer.
>
> Yes, you may be stating the obvious... for some people, but it sure
> makes it more clear when you use the terms later. Also, the obvious is
> in the mind of the reader, based on previous experience with overloaded
> terms like "set."
>

This seems good.

>
> I also think the documentation should provide a Venn diagram, or some
> other method to easily identify the superset membership of the named tag
> type sets. Specifically, it is not obvious how the library
> differentiates function_pointer and member_function_pointer. Some may
> think that member_function_pointer is a subset of function_pointer (even
> though one of the examples has an assertion somewhere that checks this
> relationship).
>

I see.

>
>
>
>>This version doesn't use static assertions but e.g.
>>
>> function_type_arity<int> has no member named 'type' or 'value'
>>
>>This tells the user that function_type_arity sees a wrong argument.
>>
>>The line number of the error message is within the user's code (where
>>it happens) and not somewhere within the Function Types library (as
>>it would be using static assert, which further stops the compilation
>>process in some contexts).
>>
>>In fact, I did not invent this technique, it can be said to be common
>>practice...
>
>
> Compile time assertion, or compiler error: doesn't matter, as long as

Well, I believe good error reporting does matter. That's why the current design
was chosen.

> "bad" code is not silently accepted. I have seen many examples of
> metafunction programming where "assumptions" are violated, yet silently
> accepted, only to yield bad runtime errors. Metafunction programming
> errors should be found at compile time, not run time. Users may
> appreciate an explicit notation.
>

There is no runtime behaviour. If you violate preconditions you get an error and
the library takes some effort to give you a nice one, too.

>
>
>>I believe you did not get my point, here:
>>
>>I'm trying to say that a user who does not know MPL would not know
>>type to pass for an "Index" and thus use the version expecting a
>>non-type template parameter of type std::size_t instead.
>>This does not assume MPL knowledge, just a bit of intelligence ;-).
>
>
> An even more dangerous assumption ;-) Maybe reordering the two
> function_type_parameter functions would help, since everyone should be
> familiar with the size_t version. Once they read that section, they
> will then come to the MPL-ish section.
>

Probably the more subtle point is the most important here: making these sections
more explicit could help.

>
>>Is there a note in the TypeTraits documentation that remove_pointer
>>cannot be used to retrieve the pointee type of a smart pointer ?
>>
>>No, because it's clear that it is a low-level facility true to the
>>very nature of the type system (in fact, making remove_pointer do the
>>above thing would severaly screw up things).
>
>
> Apparantly it is not so clear. A simple search through the boost
> archives will show several questions about how to remove the pointer
> from a smart pointer. Maybe the docs for type_traits should include

I know... And I never really understood it ;-).

> some pointers to boost::pointee. However, the type_traits library is
> not up for review here, so I'm not that concerned about its
> documentation at this moment.
>
> On that note, I'm not sure we should use existing documentation as a
> minimal standard. I think we should strive to make all documentation as
> clear as possible.
>

The "maximum standard", however, is a very subjective thing - and existing
documentation of a library dealing with a similar topic may serve as an orientation.

> In this case, your library is called function_types. Some may consider
> classes that provide operator() a function type. Others would consider
> boost::function and boost::lambda function types as well. I think it is

Well, there is a definition right in the beginning. I can add an explicit
exclusion of class types to it.

> important to provide as much information as possible, especially for how
> boost libraries relate and can (not) be used together. No, I'm not
> looking for an exhaustive list. However, I would think that considering
> possible relationships is important, especially with other boost libs.

Noted.

>>I am having difficulties to come up with good use cases here (and
>>these components do not have that much to do with each other from a
>>user's perspective, except that they all deal with functions in one
>>way or the other):
>
> I've not spent much time thinking about use cases either, but I know
> those things exist, and are related to "function types" so I am curious
> as to how they are related.
>
> Also, let's say I have some "function types" some of which are true
> function types, and others which are boost::function. All I have is
> function types (and boost::function goes a long way to make sure I can
> use it like a real function without having to know it is a function
> type).
>
> typedef boost::function<int (double, char const *)> foo_t;
>
> What will happen if I pass foo_t to the function_types library?

It depends on the function you pass it to, but it is not considered to be a
function type.

> Should boost_function be an additional tag-type, which you get by maybe
> including another header file?

No. This library only models the type system. Boost.Function (and alike) should
be allowed to use this library in the future (as higher-level components use
lower-level components not the other way around).

However, here are two possible recipes of how a Boost.Function could be analyzed
with this library:

1. AFAIK Boost.Function uses a non-templated parentheses operator therefore you
can apply a typeof operator (or Boost.Typeof) on a pointer to its operator()
function (you can do this for any functor with a non-template operator()).

2.a. The "preferred syntax" is a template parametrized with a (plain) function
type to describe its signature. Partial specialization or overloading can be
used to get this template argument from an instantiation of 'boost::function'.

2.b. The "compatible syntax" is a template parametrized with the sub-types of
its signature. There is an (even documented) "corner-case-feature" of
'function_type_signature' that allows you to grab the argument list from a class
template instantiation. Because of this 'boost::functionN' is a valid signature
for 'function_type' (given that 'function_type_signature.hpp' is included)
together with the tag 'plain_function' to get the same type that a
(preferred-syntax) 'boost::function' is instantiated with.

>
> It seems that I should be able to use the function type library to unify
> parameter access, etc. For example,
> function_type_parameter_c< foo_t, 1 >::type
> should work whether foo_t is typedef'd as above, or as:
> typedef int foo_t(double, char const *);
>

As explained above the implementation for something like this for Boost.Function
is rather simple.

If you wanted to implement a utility for unified parameter access - it's pretty
much about probing conventions and this is really something totally different
than what this library is about. However it can help you a great deal for
supporting the "real" functions.

While I do not doubt the usefulness of such a utility, we need a fundament for
things like this first, true to the nature of the type system.
TypeTraits lacks to support the types handled by this library in "full beauty",
so this is the reason why it exists.

It's quite bit of work that every developer of components, like the one
mentioned above, has to spend on writing a more-or-less complete version of this
library as an implementation detail. Not to mention compile time efficiency if
many of them are used simultanously.

>
>
>>I'm not convinced of this is of any use either, though.
>>
>>Using a pointer to parentheses-operator (on functors where it is not a
>>template function) probably gives a nice example.
>
>
>
> Again, I'm not saying that there are necessarily uses for any of this.
> However, Boost is a collection of libraries. If the function_type
> library were a stand alone library, it would not matter, but it is part
> of a collection, and I think any relationships with other libs should be
> documented (and any possible implementation relationships should be
> explored for possible implementation -- for example boost::function).
>

Right. The upcoming versions of these libraries and of course the ones yet to
come should be encouraged to use this.

>
>>Someone who doesn't know a problem is not looking to solve it...
>
>
> The problem may already be solved differently (either at runtime, or via
> code generators, etc). Most likely, people coming to boost are looking
> for a reason to START using metatemplate programming. From reading the
> list for a long time, I'd be willing to bet that most boost users came
> to boost for one library in particular (probably smart pointer and
> regex), and then started seeing uses for other libs.
>
>
>
>>>FWIW, most boost libraries fail in this regard, in my opinion.
>>>Library documentation should make as few assumptions about the
>>>knowledge and experience of the user as possible.
>>>
>>
>>Well, I disagree here:
>>
>>Is the decision whether or not to use a software components really a
>>matter of being able to implement it ?
>>
>>I believe this is a very onesided view: Given enough time, pretty much
>>
>>everything is implementable and finding an elegant and suitable design
>>to encapsulate things can be much harder.
>>
>>Everyone knows the differences of file systems on different platforms
>>and can come up with a half-baked solution to make things portable
>>and I guess everyone who uses lexical_cast knows how to convert
>>integers to strings without it.
>>
>>So why are we using generic components at all ?
>>
>>Because it simplifies our code and makes it more expressive, probably
>>adds to its runtime efficiency, because we can subscribe to the
>>design and because it's a lot of work to get things right out on our
>>own and because the libraries handle subtle problems we would only
>>become aware of when implementing things ourselves.
>
>
> I agree with everything you said here, so I'm not sure where we
> disagree...
>
>
>
>>>I like them there, but they do introduce questions about usage. In
>>>particular, you need to leave "kind" because as far as I see, it is
>>>the only way to get the tag identity of a signature.
>>
>>You should not use it directly in 99% of all cases and use
>>
>> is_function_kind<Tag, T>
>
>
> Hmmm. I do not see this anywhere in the online documentation.
>

<CITE>

  template<typename Tag, typename T>
  struct is_function_type;

</CITE>

In fact it's in there two times ;-).

>
>>instead. Actually there are no public functions to properly compare
>>the tags so you should stay away from this.
>
>
> Actually, I was thinking of something more along the line of...
>
> is_function_type<
> function_type_signature< foo_t >::kind,
> bar_t >::value
>
> though function_kind would be nicer...
>

No - just simply: is_function_type<Tag,T>.

There really should be a note that these members are there, because they are
there and are not at all required for basic usage. It leads to too much confusion.

There also should be a note that 'function_type_signature' is only needed if you
need a full representation in a single type (i.e. a Sequence of sub-types with
some additional properties). The "Encapsulation" section states this but I bet
it could do this stronger.

In most cases you won't need this class at all. Again, structuring into two
sections may help.

>
>
>>>I question the "types" because I think it could be a little easier
>>>to get the return type, and the parameters without the optional
>>>class sandwiched inbetween.
>>>
>>
>>Actually it _is_ a lot easier:
>>
>> function_type_signature<T>
>>
>>is a model of MPL Sequence.
>>
>>No need to mess with the 'types' member, unless for optimization (e.g.
>>you want a mutable sequence and don't want dependencies to
>>function_type.hpp).
>
>
> I guess, from just reading, I do not see how you know if you should
> "skip" the optional "class" member of the sequence, without some sort of
> conditional code.
>

Sub-types include: result type, followed by the class type (if present) followed
by parameter types.

If you only want parameter types use 'function_type_parameter' (a single one)
'function_type_parameters' (all of them).

If you want the result type use 'function_type_result' if you want the class use
'function_type_class'.

Only if you need all at once use 'function_type_signature'.

Not sure I understand you correctly, here.

>
>
>>The problem is that most real-world use cases do involve more of the
>>library than just a single function, so it'll be harder to understand
>>what a particular function does, this way.
>
>
>
> Right. I think we are on the same page here. Leave the current
> examples, but add a unified, real world example as well.
>

On its way.

>>This said, I just mentioned some doubt if the novice you are (or not
>>are, but pretending to be for the purpose of bulletproofing the
>>documentation) has a need for this library. No intentional offense,
>>here.
>
> I'm not necessarily pretending, because I do not use these techniques
> very often. However, I am trying to read with the view of "how can the
> docs be improved", rather than "do the docs meet a minimum requirement."
>
> However, you gotta try hard to truly offend me. Unfortunately, I've
> found that I can offend people without even trying, which is one reason
> I tend to stay off the radar...

I must admit I had a strange feeling about the first rather fuzzy post.

However, I believe we have located quite a few concrete spots that need
improvement and ideas on how to approach these issues.

Thank you very much for your detailed elaboration.

Regards,

Tobias


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