|
Boost : |
From: David Abrahams (dave_at_[hidden])
Date: 2005-06-13 11:13:36
Just starting from the beginning:
This library contains .inl files. In my opinion these should be .hpp
files, but if you insist on a different extension, please make them
.ipp. Nico Josuttis made the argument for .cpp/.hpp in the early days
of Boost and it remains a good one: it's easier to find all the source
files with various tools, and you know which ones contain C++ source.
,----
| Use fewer words
|
| - E.B. White
`----
Overview
This library provides a metaprogramming facility...
I think you should drop the word âmetaprogrammingâ here. Lots of
people who don't think of themselves as doing metaprogramming might be
interested in this library and could easily be scared off. Also, it
adds nothing to the essential meaning of the sentence
For the purpose of this documentation, these types are collectively
referred to as function types (this differs from the standard
definition and redefines the term from a programmer's perspective to
refer to the most common types that involve functions, however,
classes with an overloaded parentheses operator are not considered a
function type, here).
Put the parenthesized material in a footnote. It's too long to be
part of that sentence and anyway it's too much detail for 99% of
readers, who won't care about the standard's definition.
The classes introduced by this library shall conform to the
concepts of the Boost Metaprogramming library (MPL).
This library introduces templates, not classes.
Drop the word âshall.â It's unnatural (âshallâ is normally used in
declarations of what will happen in the future) and adds nothing.
doesn't really conform to the way the C++ standard uses it, which is a
statement of requirements on what implementations of the standard must
do -- unless of course you think you're writing a standard that other
people will implement.
Also, you don't say which concepts. I would just say, âThis library is
designed to work well with the Boost Metaprogramming library (MPL),â
or say earlier on âThis is a library of metafunctions <link to MPL
concept definition>,â if that's accurate. Yes, I know this somewhat
contradicts my admonition to drop âmetaprogramming.â
The Function Types library enables the user to:
* test an arbitrary type for being a function type of specified
kind,
âtest whether a type is a specific kind of function typeâ
* inspect properties of function types, such as function arity,
result parameter types, etc.
Drop the 1st comma
* view and modify sub types of an encapsulated function type with
MPL Sequence operations, and
No space in âsubtypesâ (sub is not a word on its own -- repeated
throughout the document). What is an âecapsulated function type?â
* synthesize function types.
A tiny bit more detail about what this means should go here. I am
guessing it means
function_ptr<R, A1, A2>::type ==> R(*)(A1,A2)
That would be âsynthesize function types from argument and return
types.â
Motivation
Applying metaprogramming to increase the flexibility of software
design often involves the inspection and manipulation of function
types.
A very common application is the automatic generation of callback
facilities which occurs frequently in Boost libraries, such as Bind,
Lambda, Phoenix and Function.
This is a very weak opening. A person reading the Motivation section
wants to know, âwhat would I do with this library?â I suggest
something like:
Generic libraries that accept callable arguments are common in C++:
STL, Boost.Lambda, Boost.Function, and Boost.Python are just a few
examples. Analysis and manipulation of built-in function types
seems to appear in each one.
The above is by no means perfect, and it took me 5 minutes to write.
It can be hard to follow Mr. White's advice ;-)
Other (often related) applications, include creating interface
descriptions from function prototypes (extracting meta information
about an interface).
That's so redundant it's not worth saying. All the periods in this
paragraph should be commas.
concept checking (e.g. find out if two
functions model a setter/getter pair). and systematic overload
selection (creating a function pointer to e.g. std::sin requires a
cast to either select the overload for float, double or long
double).
Other applications include
- concept checking
[Show brief example asserting that two functions model a
setter/getter pair]
- systematic overload selection
[Show brief example of how creating a function pointer to
.g. std::sin requires a cast to either select the overload for
float, double or long double]
Currently Boost libraries redundantly use template partial
specialization or overloading cascades to deal with function
types. This has the following disadvantages:
Can't use âthisâ without an antecedent. âThis approach has the
following disadvantages:â
* It is tedious to write when the library is implemented,
It is tedious to implement.
* most libraries fail to support functions with non-default
calling convention or variadic parameter lists
* proper support for the former point results in redundant work
Drop "point"
for the authors, redundant per-library configuration settings
Need a comma right here.
and redundant code being processed if these libraries are used
in the same program.
This library gives developers a powerful tool at hand to avoid the
above problems.
Drop "at hand." But I'd probably drop the whole sentence.
It covers the functionality of the function_traits class (introduced
by the Boost Type Traits library), which has been agreed to be
unsuitable for template metaprogramming in most situations, due to
the fact that its interface encodes the index to function paramter
types in identifier names and the inability to work with types other
than plain non-member/static functions, which, from today's
perspective, makes function_traits a weak spot of the otherwise
excellent library.
This sentence is too long! I suggest:
This library provides all the functionality of the function_traits
<link> class template from the Type Traits library in a form that's
more suitable for template metaprogramming.
If you like, add a link to a detailed explanation of the reasons that
function_traits is unsuitable, in a setting where you have the space
to write something comprehensible to non-gurus. There's no room to
say anything useful about that in the introductory section of your docs.
Other Type Traits classes covered as special cases by this library's
is_function_type class are is_function and
is_member_function_pointer.
"Also, is_function and is_member_function_pointer are covered as
special cases by this library's is_function_type class template."
---------------
Interface
Some simple examples are missing right here. In fact, I've gone well
past a whole page of reading with no examples. I don't think we
should introduce any new libraries without a simple example in the 1st
page. The rest of the doc has some examples, but only as linked
documents. I haven't clicked through, but I imagine they're complete,
compiling programs. That's too much information, too distantly
located, to be useful to the person trying to get a feel for the
library. Note also that many people will be reluctant to click
through those because it will launch their IDE instead of displaying
in their browser.
---------------
Synopsis:
All this stuff goes in namespace boost? I don't think I'm happy about that.
All names are actually defined inside local namespaces. They are
injected to the boost namespace via a using directive.
Okay, that's a little better. It avoids some ADL problems but still
brings many special-purpose names into the top-level namespace. Do
the tag types belong there? Also "local namespace" isn't a
well-accepted term. How about "sub-namespaces of boost?"
I don't think the synopsis should show them in namespace boost and
then be amended by a footnote at the bottom that many people might not
read. At least put a link to the note in the synopsis code.
template<typename F>
struct function_type_arity;
template<typename F>
struct function_type_result;
template<typename F>
struct function_type_class;
template<typename F, typename I>
struct function_type_parameter;
template<typename F, long I>
struct function_type_parameter_c;
template<typename F>
struct function_type_parameters;
Yowch! I really hate to see a boost.whatever library with names like
whatever_foo
whatever_bar
whatever_baz
in it. Isn't this what namespaces are for?
The [...see Tag Types...] link just takes me to the top of the doc.
----------------
Headers
Header file names generally match the name of what their inclusion
causes to be defined with the exception of
function_type_parameter.hpp which also contains the class
function_type_parameter_c.
"With one exception, each component is defined in a header file whose
name is the same as that of the component, with an .hpp suffix. For
example, before using is_function_type, your functions should first
contain:
#include <boost/function_types/is_function_type.hpp
The exception to this rule is function_type_parameter_c, which is
defined in boost/function_types/function_type_parameter.hpp."
The tag types are defined as soon as the first header containing a
class that depends on them is included.
The headers don't contain classes.
At this point, I have no clue what these dependencies are and can't
understand what this statement means. All I can guess is that I don't
ever have to worry about specific #includes for the tag types. If
that's the case, just tell me so :)
------
Tag types
As stated at the beginning of this document, there can be different
kinds of function types:
# non-member/static function,
# pointer to non-member/static function,
# reference to non-member/static function,
# member function pointer,
# member function pointers that are const-volatile qualified,
# each of the above with a parameter list ending with an ellipsis operator, and
# each of the above for every calling convention defined by the C++ implementation in use.
The Function Types library uses tag types to describe differnt kinds
of function types, and arbitrary supersets of these and their
inverse. [Show an example of a simple tag type in use]
You can't represent "arbitrary supersets;" you only represent the
supersets that you've chosen to support.
"Each kind of function type has a corresponding tag type:
* non-member/static functions: plain_function
* pointers to non-member/static functions: function_pointer
* references to non-member/static functions: function_reference
...etc...
There are also tags to represent groups of function types:
* Any function type at all: any_function
...etc..."
The document doesn't say anywhere what no_function is.
It has no relevance to the user, how these tags are implemented
Drop the comma.
except that they exist and that they are types that can be passed
as template type arguments.
--------
Classification
I always learned that it is bad form to have a section with only one
subsection. You have several of these.
I would list all of the metafunctions except for the decomposition
metafunctions at the top level and have a section called
"Decomposition Metafunctions". Except there seems to be something
missing alongside is_function_type: I want a metafunction that
returns the tag corresponding to a given function type's kind.
----
is_function_type
Members
value - Static constant of type bool, evaluating to true if T is
Uhm, the essential member of any metafunction is ::type, and I don't
see it listed here. Value doesn't "evaluate to true", it "is true."
an element of the set of types specified by Tag. Member function
pointers may be more const-volatile qualified than specified,
Hmm, have you looked carefully at the use cases for this? Member
functions often have counterintuitive is-a relationships. For
example, a base class member pointer is-a derived class member
pointer, but not vice-versa. It's not obvious to me that any is-a
relationships should hold here. What are the use cases?
Need "and" right here.
top-level const-volatile qualification of pointers is always
ignored. Unless explicitly specified otherwise by the Tag, both
"Unless the tag explicitly indicates otherwise, ..."
variadic and non-variadic function types of any calling conventions
"convention"
are matched.
... - See the MPL Integral Constant concept.
What?? What kind of member is "..."?? This is unacceptably vague.
Header
<boost/function_types/is_function_type.hpp>
Member function pointers may be more const-volatile qualified than
specified
---------
Decomposition
Six class templates are provided to extract properties of a function
type, such as arity, parameter-, class (where appropriate)- and
result type.
"Six class templates are provided to extract properties of a function
type, such as arity and the types of the result, parameters, and the
class targets of member functions."
-------
template<typename T>
struct function_type_arity;
Template parameters
T - The function type to be inspected. T must be a function type as
defined at the beginning of this document
(is_function_type<no_function,T>::value == false).
You shouldn't repeat that verbose description all over the place.
"T - Any function type <link to definition>"
is better and more concise.
Members
value - Static constant of type size_t, evaluating to the number
of parameters taken by the type of function T describes. The hidden
this-parameter of member functions is never counted.
That particular behavior does not match the needs of any use cases
I've seen. I always want to treat the hidden this parameter as the
function's first parameter (actually as a reference to the class, with
cv-qualification given by that of the member function itself). What's
the rationale?
If T describes a variadic function prototype (e.g. printf) the
ellipsis parameter is not counted.
--------
template<typename T, typename I>
struct function_type_parameter;
Template parameters
...
I - The index to select the parameter type to be returned (MPL
Integral Constant). Valid indices start with zero (for the first
parameter).
Putting "MPL Integral Constant" in parens has no agreed-upon meaning
and seems arbitrary and needlessly terse.
"I - An MPL Integral Constant describing the index of the parameter
type to be returned. The index of the first parameter is zero."
What about the hidden "this" parameter? I think you know the
semantics I want, but you don't say what you provide.
--------
Encapsulation
While the decomposition part of the interface provides a fine
Need a hyphen here.
grained set of traits classes to find out distinct aspects of a
^^^^^^^^
"discover," or "query"
function type, specializations of the class template
function_type_signature in contrast represent a function type in
order to work with it as a whole.
Therefore function_type_signature can be used with MPL Sequence
operations to work with the representee type's sub types.
^^^^^^^^^^^
"represented"
This begins to sound like function_type_signature<T> is an MPL
sequence. I would just drop the sentence. Oh, wait, after reading
the whole description I now believe it is a Random Access
Sequence... but you never tell me what the sequence contains!! This
is a serious omission.
Other properties, such as arity, kind of function type and the
representee type, are contained as type members. So
^^^^^^^^^^^
"represented". Turn the period into a comma.
function_type_signature can also be used as a traits
bundle. However, this class template should be seen as a white box,
I don't know what that means. Please use a more generally understood
term, or just drop that part of the sentence.
so using the primary interface for clasification and decomposition
Drop "using"; you repeat "use" below.
is recommended for general use.
I don't know what you mean by this recommendation. If I have a
specialization of function_type_signature, how am I supposed to get
information about it, or use it? Am I supposed to pass it to the
decomposition metafunctions (I don't think so!)?
---- BTW, the use of "Ts" as a parameter is confusing. I keep reading the "s" as a subscript. It's not a list of Ts that's being passed; it's a list of "Types." Why not just use "Types?" --- template<typename Tag, typename Ts> struct function_type; Needs a brief description; maybe all do. Example: "Returns a new function type built from a sequence of types." Template parameters Tag - Tag type specifying the kind of function type to be created. Abstract tag types (ones that describe abstract supersets of types such as any_function) are not allowed with the exception of no_function (see below for effect). "Tag types that describe abstract supersets of types, such as any_function, are not allowed with the exception of no_function (see below<link>)." Ts - Model of MPL Forward Sequence of sub types, starting with Drop "Model of" the result type optionally followed by the class type (if Tag specifies a member function pointer type) That sounds like if Tag specifies a member function pointer you have the option to represent the class type. Just drop "optionally" and remove the parens. So here you are using the form I like, where class types are treated just like any other (should be a reference, though). Why not do this uniformly. followed by the parameter types, if any. If Ts is not a sequence (mpl::is_sequence<T>::value == false), a specialization of function_type_signature for this type is used. This is unintelligible. You already said Ts is required to be a sequence. Now you're taking it back? A specialization of function_type_signature for *which* type is used? Used how? This may fail if function_type_signature is an incomplete template due to forward-declaration without definition. Why would that ever be the case? Shouldn't your library take care of it? Members type - The function type as specified by the template arguments. "The synthesized function type" Unless explicitly specified otherwise by Tag, type is a non-variadic function of default calling "convention" . If Tag is no_function, type is identical to Ts if Ts is an MPL Sequence other than a specialization of function_type_signature, in which case an type is an mpl::vector (this is primarily for internal use of the library but, since it may be useful in some situations, there is no reason to hide it from the user). Oh yes there is!! I can't for the life of me understand these semantics. Guideline: anything whose specification is too complicated to explain easily probably needs to be redesigned. Also, special-case "if" clauses, especially those that test whether something matches a concept (like "is_sequence") tend to destroy genericity. ---- Portability Currently this library does not contain any workarounds for broken compilers. It was successfully tested with Microsoft Visual C++ Versions 7.1 to 8.0, Intel Linux Versions 7.0 to 8.1 and GCC Version 3.2 to 4.0. The library is expected to work with compiler using the Edison Design Group frontend other than Intel, although currently there is no proof for this. Okay, I appreciate the desire to avoid broken compiler workarounds. However, many of the libraries in Boost that could use this library do support broken compilers, and therefore -- I think -- are not using idioms that can readily leverage metafunctions for this purpose. The true measure of whether this library should be accepted, IMO, is in whether it can be used to eliminate large amounts of code from other libs. Can it? The quickest way to a proof-of-concept is to apply it. Incidentally, I think http://lists.boost.org/MailArchives/boost/msg81758.php makes it possible to implement almost any trait we previously thought was impossible in pre-7.1 Visual C++. I'm not ready to accept this library, and I'm not ready to reject it either. It has the potential to be extraordinarily useful, but that's unproven as far as I'm concerned. I have some substantial complaints with the interface, and I have some serious problems iwth the docs as you can see (not all of those remarks are trivial grammatical edits!) -- Dave Abrahams Boost Consulting www.boost-consulting.com
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk