Boost logo

Boost :

From: Jody Hagins (jody-boost-011304_at_[hidden])
Date: 2005-06-07 10:48:53


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

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

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

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

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

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

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

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

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

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 *);

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

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

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

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

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

> I'll try to add more real-world examples.

Great! Just between you and me, this is a truely lacking area in most
boost libraries (with a few exceptions, like smart pointer).

> In fact, I don't think a pathological interest in torturing compilers
> is a must to be a good programmer. It leads to some interesting

That's great! I'm going to have to remember that one!

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


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