[function types][review] REMINDER

This is to remind everyone that we have a review going on at present for Tobias Schwinger's Function Types library. Tentatively the review period has been extended until 20th June, which gives everyone a week more to take a look at Tobias submission, I'd really like to encourage as many folks as possible to take a look before then. Here are the details again: Available from the sandbox at: http://boost-sandbox.sourceforge.net/vault/ or the zip file is: http://boost-sandbox.sourceforge.net/vault/index.php?action=downloadfile&filename=function_types.zip&directory=& This library provides a metaprogramming facility to classify, decompose and synthesize function-, function pointer-, function reference- and member function pointer types. 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). The classes introduced by this library shall conform to the concepts of the Boost Metaprogramming library (MPL). The Function Types library enables the user to: * test an arbitrary type for being a function type of specified kind, * inspect properties of function types, * view and modify sub types of an encapsulated function type with MPL Sequence operations, and * synthesize function types. This library supports variadic functions and can be configured to support non-default calling conventions. (The following is taken from : http://www.boost.org/more/formal_review_process.htm ) Here are some questions you might want to answer in your review: What is your evaluation of the design? What is your evaluation of the implementation? What is your evaluation of the documentation? What is your evaluation of the potential usefulness of the library? Did you try to use the library? With what compiler? Did you have any problems? How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? Are you knowledgeable about the problem domain? And finally, every review should answer this question: Do you think the library should be accepted as a Boost library? Be sure to say this explicitly so that your other comments don't obscure your overall opinion. John Maddock (Function Types review manager).

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

On Mon, 13 Jun 2005 12:13:36 -0400 David Abrahams <dave@boost-consulting.com> wrote: This message is VERY difficult to read, because of the formatting. At first, I thought it might have been HTML (I do not like HTML, so my reader converts it to plain text before showing it). However, the only thing I could find in the raw email was a header that said it converted from base64 to 8bit. I imagine it could still be a problem with my reader, but I usually do not have problems with mail from you, so I thought I'd let you know, just in case...

On Mon, 13 Jun 2005 12:57:03 -0400 Jody Hagins <jody-boost-011304@atdesk.com> wrote:
At first, I thought it might have been HTML (I do not like HTML, so my reader converts it to plain text before showing it). However, the only thing I could find in the raw email was a header that said it converted from base64 to 8bit.
Looking at the message source again (in vi), I also see some really strange looking characters. For example, certain words and phrases are surrounded with a 3-byte prefix of { 0xe2, 0x80, 0x9c } and a 3-byte postfix of { 0xe2, 0x80, 0x9d }. Of course, this does not explain the other strangeness of the formatting.

Jody Hagins <jody-boost-011304@atdesk.com> writes:
On Mon, 13 Jun 2005 12:57:03 -0400 Jody Hagins <jody-boost-011304@atdesk.com> wrote:
At first, I thought it might have been HTML (I do not like HTML, so my reader converts it to plain text before showing it). However, the only thing I could find in the raw email was a header that said it converted from base64 to 8bit.
Looking at the message source again (in vi), I also see some really strange looking characters. For example, certain words and phrases are surrounded with a 3-byte prefix of { 0xe2, 0x80, 0x9c } and a 3-byte postfix of { 0xe2, 0x80, 0x9d }.
Of course, this does not explain the other strangeness of the formatting.
Okay, sorry, those are unicode quotation marks. Let me try a repost. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Jody Hagins <jody-boost-011304@atdesk.com> writes:
This message is VERY difficult to read, because of the formatting. ... I imagine it could still be a problem with my reader, but I usually do not have problems with mail from you, so I thought I'd let you know, just in case...
FWIW, It's not just your reader. It came up rather badly for me too. -- Chris

David Abrahams <dave@boost-consulting.com> writes:
* 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?”
Oh, and I don't think "subtypes" is the correct term either. I'd just say "parameter and return types." -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave@boost-consulting.com> writes: 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. ---------
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

From: David Abrahams <dave@boost-consulting.com> Thanks for the repost. I, too, can read it now.
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
Absolutely right.
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 ;-)
I question why you say "seems to appear." It should be possible to state it definitively, right? That aside, this is still lacking. It tells me that powerful tools that one might consider black boxes need the functionality of this library, but says nothing about why the reader may.
"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
s/functions/code/ -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart <stewart@sig.com> writes:
From: David Abrahams <dave@boost-consulting.com>
Thanks for the repost. I, too, can read it now.
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
Absolutely right.
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 ;-)
I question why you say "seems to appear." It should be possible to state it definitively, right?
Only if you're referring to the libs listed and not all the generic libs that accept callable args. So if you can clarify that, be my guest.
That aside, this is still lacking. It tells me that powerful tools that one might consider black boxes need the functionality of this library, but says nothing about why the reader may.
I can only think of needing this library if I were going to build a generic library that accepts callable arguments. Whether or not that is a powerful black box is a matter of opinion I guess.
"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
s/functions/code/
Right. -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
From: David Abrahams <dave@boost-consulting.com>
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 ;-)
I question why you say "seems to appear." It should be possible to state it definitively, right?
Only if you're referring to the libs listed and not all the generic libs that accept callable args. So if you can clarify that, be my guest.
How about this: Generic libraries that accept callable arguments are common in C++; analysis and manipulation of built-in function types seem to appear in each one. Some examples are the STL, Boost.Lambda, Boost.Function, and Boost.Python. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart <stewart@sig.com> writes:
From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
From: David Abrahams <dave@boost-consulting.com>
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 ;-)
I question why you say "seems to appear." It should be possible to state it definitively, right?
Only if you're referring to the libs listed and not all the generic libs that accept callable args. So if you can clarify that, be my guest.
How about this:
Generic libraries that accept callable arguments are common in C++; analysis and manipulation of built-in function types seem to appear in each one. Some examples are the STL, Boost.Lambda, Boost.Function, and Boost.Python.
"Examples are A, B, and C" is weird. How about: Generic libraries that accept callable arguments are common in C++; analysis and manipulation of built-in function types seem to appear in each one. The STL, Boost.Lambda, Boost.Function, and Boost.Python are just a few libraries that fit this pattern. -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
How about this:
Generic libraries that accept callable arguments are common in C++; analysis and manipulation of built-in function types seem to appear in each one. Some examples are the STL, Boost.Lambda, Boost.Function, and Boost.Python.
"Examples are A, B, and C" is weird. How about:
I don't know what's weird about it, but we each have our own sensibilities.
Generic libraries that accept callable arguments are common in C++; analysis and manipulation of built-in function types seem to appear in each one. The STL, Boost.Lambda, Boost.Function, and Boost.Python are just a few libraries that fit this pattern.
That's fine, too. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart <stewart@sig.com> writes:
From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
How about this:
Generic libraries that accept callable arguments are common in C++; analysis and manipulation of built-in function types seem to appear in each one. Some examples are the STL, Boost.Lambda, Boost.Function, and Boost.Python.
"Examples are A, B, and C" is weird. How about:
I don't know what's weird about it, but we each have our own sensibilities.
Trees are Elm, Oak, and Maple. Sounds like Yoda. Weird these speech constructs are. -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
From: David Abrahams <dave@boost-consulting.com>
Rob Stewart <stewart@sig.com> writes:
How about this:
Generic libraries that accept callable arguments are common in C++; analysis and manipulation of built-in function types seem to appear in each one. Some examples are the STL, Boost.Lambda, Boost.Function, and Boost.Python.
"Examples are A, B, and C" is weird. How about:
I don't know what's weird about it, but we each have our own sensibilities.
Trees are Elm, Oak, and Maple.
Sounds like Yoda. Weird these speech constructs are.
Please note that the original was "Some examples are...." That isn't weird to me. I've heard and read that phrasing in many contexts. If you omit "some" it does have a slightly unusual character, but it still isn't "Examples the STL, Boost.Lambda, Boost.Function, and Boost.Python are." -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart <stewart@sig.com> writes:
Trees are Elm, Oak, and Maple.
Sounds like Yoda. Weird these speech constructs are.
Please note that the original was "Some examples are...." That isn't weird to me. I've heard and read that phrasing in many contexts. If you omit "some" it does have a slightly unusual character, but it still isn't "Examples the STL, Boost.Lambda, Boost.Function, and Boost.Python are."
Some trees are Elm, Oak, and Maple. Weird they still are. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Hello Dave, first of all: thank you very, very much for these numerous hints on how to improve the documentation - especially for taking the time to rewrite several passages. Amazing work! Mind if I just "merge" some of this into the next revision? I'll cut down your post for the reply to highlight the points I want to answer to -- and add the others to my todo list. David Abrahams wrote:
[...]
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.
I was not too sure about this myself. I did this because most libraries put their primary interface into namespace boost.
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?
Okay - if we don't put them in namespace boost, we can also use a different naming for these. I like it, especially because the names are currently too lengthy for my taste.
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."
is_function_type<Tag,T> is an MPL Integral Constant. The type member is the identity of its specialization and not listed explicity because it's part of the concept. It won't hurt, I guess.
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?
I'm thinking about removing cv indication from the tags and cv-qualifying the class type, instead (see comments in the examples interpreter.hpp : line 99 and function_closure.hpp : line 120).
---------
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?
The rationale is that there is no unified call syntax for member function pointers and non-member/static function pointers, anyway: Typically we use a cascade of template specializations that does the actual invocation for different function arities. Only counting "real" parameters here allows us to build such a cascade for arities ranging from zero to "OUR_MAX_ARITY_LIMIT" without having to deal with the two special cases that there are no nullary member functions and that we need to go up to OUR_ARITY_LIMIT+1 for member function invocations if the context reference is taken into account. Think about generating the (hand-written) cascade in libs/function_types/example/interpreter.hpp : line 299 with Boost.Preprocessor, for example. Even if completely binding a function (which I believe is a rather seldom use case), we still need a distinction between a member and non-member function pointer which won't go away no matter how we put it. Further we can still take the size of function_type_signature (well, in this case the result type is counted as well).
"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.
The same argumentation applies here. And parameters indices should be consistent with the function arity. Further it makes client code more expressive. I guess it's your turn here to prove my design is faulty ;-). If you can convince me, I'll happily change things, of course.
[...]
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!)?
Use it as an MPL sequence. I added this recommendation because it led to so much confusion discussing this with Jody Hagins (and I probably made things worse). The recommendation refers to using it as a traits bundle: e.g. function_type_signature<T>::arity instead of function_type_arity<T> which should be preferred.
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.
See above.
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
I remember being rather unhappy about this writing it...
function_type_signature for *which* type is used? Used how?
... it needs to be rewritten.
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?
Both 'function_type_signature' (which is the backend for all higher-level inspection components) and function_type need quite a bit of code (and when used in preprocessing mode a significant ammount of compile time). That's why I decided to not let one depend upon the other, but to make them plug into each other when both are defined.
. 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.
It used to be a bit more understandable before I removed a rather strange feature to grab the signature from a class template's parameter list. I will just remove this as well.
Also, special-case "if" clauses, especially those that test whether something matches a concept (like "is_sequence") tend to destroy genericity.
Would you mind explaining this in some more detail?
----
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.
Okay. The desire to avoid broken compiler workarounds applies especially to the review version. I had hoped the review to answer how much portability is required for proper Boost integration. I definitely plan on extending the list of supported compilers.
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.
Good idea!
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++.
If the folks at Borland don't fix their compiler they should at least add this as a new bug...
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!)
Summary: - proof it by making some boost libraries use it - change naming - change docs I hope (but am not entirely sure) it's all doable within the review period. Any hints on a preferred prioritization? Regards, Tobias

Tobias Schwinger wrote:
Mind if I just "merge" some of this into the next revision?
That is, along with another sentence in the Acknowledgements section, of course.
[...] is_function_type<Tag,T> is an MPL Integral Constant. The type member is the identity of its specialization and not listed explicity because it's part of the concept. It won't hurt, I guess.
^^^^^^ Should mean: Listing them explicity won't hurt (not the fact that they are not).

Tobias Schwinger <tschwinger@neoscientists.org> writes:
Hello Dave,
first of all: thank you very, very much for these numerous hints on how to improve the documentation - especially for taking the time to rewrite several passages. Amazing work!
My pleasure.
Mind if I just "merge" some of this into the next revision?
Go for it.
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?
Okay - if we don't put them in namespace boost, we can also use a different naming for these. I like it, especially because the names are currently too lengthy for my taste.
Great.
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."
is_function_type<Tag,T> is an MPL Integral Constant.
You don't say that anywhere.
The type member is the identity of its specialization and not listed explicity because it's part of the concept. It won't hurt, I guess.
It's necessary. Until you say it is an Integral Constant, there's no relationship required between ::type and ::value, so even if it's a metafunction you had left its result undefined. More importantly, few readers are likely to be intimately familiar with the MPL concepts, and most will miss the connection.
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?
I'm thinking about removing cv indication from the tags and cv-qualifying the class type, instead (see comments in the examples interpreter.hpp : line 99 and function_closure.hpp : line 120).
Not gonna look at the source, sorry. No time.
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?
The rationale is that there is no unified call syntax for member function pointers and non-member/static function pointers, anyway:
Yes, but I typically build something that *does* have a common call syntax when wrapping member functions, i.e. I build a function object. I would prefer if the metafunction would reflect its signature rather than forcing me to assemble it from this nonuniform blob.
Typically we use a cascade of template specializations that does the actual invocation for different function arities.
Whaa? I don't have any "cascading" template specializations in Boost.Python AFAIK. I don't think Boost.Bind does either.
Only counting "real" parameters here allows us to build such a cascade for arities ranging from zero to "OUR_MAX_ARITY_LIMIT" without having to deal with the two special cases that there are no nullary member functions and that we need to go up to OUR_ARITY_LIMIT+1 for member function invocations if the context reference is taken into account.
Okay, I've dealt with that issue. But it's a (really) minor one. Generating these things with the preprocessor should usually be done with vertical repetition (you'll get lousy compile times and no debuggability otherwise), which makes that sort of iteration bounds adjustment trivial.
Think about generating the (hand-written) cascade in
libs/function_types/example/interpreter.hpp : line 299
with Boost.Preprocessor, for example.
Sorry, no time to look at the code. But anyway, I've been there. It's easy.
Even if completely binding a function (which I believe is a rather seldom use case), we still need a distinction between a member and non-member function pointer which won't go away no matter how we put it.
I agree with that but don't see the relevance.
Further we can still take the size of function_type_signature (well, in this case the result type is counted as well).
?? You mean sizeof()? or mpl::size<>?
"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.
The same argumentation applies here.
I still disagree with it :) But regardless, **you need to document the semantics you do provide**.
And parameters indices should be consistent with the function arity.
I agree with that, but don't see the relevance. In my world, the arity of int (foo::*)(int) is 2.
Further it makes client code more expressive.
On what do you base that claim?
I guess it's your turn here to prove my design is faulty ;-).
It's not so much faulty as needlessly irregular. I believe that will be an inconvenience in some real applications, and at the very least will cost more template instantiations than necessary.
If you can convince me, I'll happily change things, of course.
How'd I do?
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!)?
Use it as an MPL sequence.
I added this recommendation because it led to so much confusion discussing this with Jody Hagins (and I probably made things worse).
It may just be in the phrasing. There's too much detail missing.
The recommendation refers to using it as a traits bundle: e.g.
function_type_signature<T>::arity
instead of
function_type_arity<T>
which should be preferred.
But the latter doesn't look like a "use" of function_type_signature at all! Anyway, don't clarify your meaning for mehere; propose a documentation fix. Just look very carefully at the words you used (like "use," "primary interface," "white box," etc.), and consider how they might be (mis)interpreted by someone who doesn't already know what you're talking about.
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.
See above.
Still waiting for a satisfactory answer. Seems to me that your library only gets easier to use (and learn!) if it traffics in one uniform structure.
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?
Both 'function_type_signature' (which is the backend for all higher-level inspection components) and function_type need quite a bit of code (and when used in preprocessing mode a significant ammount of compile time).
That's why I decided to not let one depend upon the other, but to make them plug into each other when both are defined.
Well, I suggest you say something that the average dummy is more likely to understand, like, "you better #include <whatever> or this won't compile."
. 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.
It used to be a bit more understandable before I removed a rather strange feature to grab the signature from a class template's parameter list.
I find it hard to believe that it was easier to understand when there was an *additional* feature in its behavior!
I will just remove this as well.
That sounds like it goes in the right direction.
Also, special-case "if" clauses, especially those that test whether something matches a concept (like "is_sequence") tend to destroy genericity.
Would you mind explaining this in some more detail?
Take boost::variant, which (at one time -- still?) would accept up to N arguments describing the held types, *OR* an MPL sequence of the types. The problem happens when your generic code wants to create a variant of one element that happens to be an MPL sequence type. Now you need a special case that sticks that sequence in another sequence. Well, in that case the sequence interface is the only one of any use to you, isn't it? The point is that switching on a type's properties often introduces nonuniformity of semantics, which hurts genericity. I didn't analyze your case to see if it was a problem here in particular.
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++.
If the folks at Borland don't fix their compiler they should at least add this as a new bug...
:o)
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!)
Summary:
- proof it by making some boost libraries use it - change naming
- Improve uniformity, if I can get it. I would probably accept the lib without that change, but I fear I will grumble every time I use it.
- change docs
I hope (but am not entirely sure) it's all doable within the review period. Any hints on a preferred prioritization?
1. Proof it 2. Post updated docs that include the proposed naming changes 3. Change the code. But I'm not too particular about it. I think the proof should come first because I'd happily accept assurances in place of steps 2 and 3 happening before the review period ends. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
is_function_type<Tag,T> is an MPL Integral Constant.
You don't say that anywhere.
Well, that's what the "member '...' notation" was supposed to mean: ... - See the MPL Integral Constant concept.
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?
I'll have to add an annotation here: I am looking at this from the perspective of pointer <-> object comparison: More CV qualification of the member function pointer is always OK, as it is OK to call a const member function on a non-const object. You need a pointer to an at least const qualified member function if the object is const.
I'm thinking about removing cv indication from the tags and cv-qualifying the class type, instead (see comments in the examples interpreter.hpp : line 99 and function_closure.hpp : line 120).
Not gonna look at the source, sorry. No time.
Never mind. It refers to a piece of code (to be more specific to a comment in front of one), which we'll get rid of in many places, when using a qualified class type to describe cv qualification of member function pointers: // Metafunction to decorate class type with cv-qualifiers. // ?! Should this perhaps be the default behaviour of function_type_class ?! template< typename MemFunPtr > struct class_of //... I believe we basically agree on this point (however I'm not sure about the reference part of your suggestion in this direction - see below).
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?
The rationale is that there is no unified call syntax for member function pointers and non-member/static function pointers, anyway:
Yes, but I typically build something that *does* have a common call syntax when wrapping member functions, i.e. I build a function object. I would prefer if the metafunction would reflect its signature rather than forcing me to assemble it from this nonuniform blob.
It seems to me you are requesting to optimize a generic library for your favourite, specific use case. And honestly, I'm not even convinced this very case will end up more optimal. Let me try to show you why: So you are basically talking about an implementation of boost::mem_fn. Let's start with this passage of a hypothetical and naive implementation: // ... template<typename MemFunPtr> struct mem_fn_wrap2 : mem_fn_base<MemFunPtr> // ^^^ holds protected member MemFunPtr val_mem_fun_ptr { typename function_type_result<MemFunPtr>::type operator() ( typename function_type_class<MemFunPtr>::type & c , typename function_type_parameter_c<MemFunPtr,0>::type p0 , typename function_type_parameter_c<MemFunPtr,1>::type p1 ) { return (c.*this->val_mem_fun_ptr)(p0,01); } }; 1. This is inefficient, we would usually want to optimize forwarding by substituting by-value parameters with const references except for scalar types (like char, int, float, pointers). 2. Now let's assume we "unify" the parameters: our class type becomes the 0th parameter 3. The above requires we use a reference for the class type so it can pass through the metafunction that implements forward optimization (without an effect but also without having to handle a special case) 4. Now we want to add an overload that takes a pointer to the context object instead of a reference ^^^^^^ ((<<BOOM>>)) A slight change of our scenario turns our code into a cryptic mess (especially when using a systematic way to generate it - i.e. Boost.Preprocessor). => This usually indicates our design is neither generic nor too well-chosen. => Conclusion: Class types _are_ special. The design should reflect this.
Typically we use a cascade of template specializations that does the actual invocation for different function arities.
Whaa?
I don't have any "cascading" template specializations in Boost.Python AFAIK. I don't think Boost.Bind does either.
Sorry for not being very precise, here. If it's not template specialization it's overloading or numbered functions or functions in numbered templates or classes (hope I got them all, now)...
Only counting "real" parameters here allows us to build such a cascade for arities ranging from zero to "OUR_MAX_ARITY_LIMIT" without having to deal with the two special cases that there are no nullary member functions and that we need to go up to OUR_ARITY_LIMIT+1 for member function invocations if the context reference is taken into account.
Okay, I've dealt with that issue. But it's a (really) minor one. Generating these things with the preprocessor should usually be done with vertical repetition (you'll get lousy compile times and no debuggability otherwise), which makes that sort of iteration bounds adjustment trivial.
This sounds like using two (slow preprocessor-) loops in client code for members and non-members, where using one would be appropriate... I agree it can be "minor", but I currently fail to see it's "less minor" than your complaint in the first place. I'm having some trouble understanding the second part of the above paragraph. What could "otherwise" possibly refer to in this context ? And isn't vertical repetition the slowest form of PP repetition there is ? Can you perhaps help me with it ?
Think about generating the (hand-written) cascade in
libs/function_types/example/interpreter.hpp : line 299
with Boost.Preprocessor, for example.
Sorry, no time to look at the code. But anyway, I've been there. It's easy.
OK. The point was that you can get away with one single and uniform, vertical repetition for both member- and freestanding/static functions. E.g: template<size_t Arity> struct invoker; template<> struct invoker<0> { // invoker function for functions // invoker function for member functions }; template<> struct invoker<1> { // invoker function for functions // invoker function for member functions }; //... template<> struct invoke< OUR_MAX_ARITY > ... (This is a simplified pseudo-code version of what I was referring to.)
Even if completely binding a function (which I believe is a rather seldom use case), we still need a distinction between a member and non-member function pointer which won't go away no matter how we put it.
I agree with that but don't see the relevance.
The relevance is, that the member function pointer will need special treatment anyway, sooner or later. We should also leave it its special properties (like habing a class type). There is no point in providing (an oversimplified, as shown above, for that matter) simplification to treat member function pointers and other function types more similar - because they are not similar!
Further we can still take the size of function_type_signature (well, in this case the result type is counted as well).
?? You mean sizeof()? or mpl::size<>?
mpl::size
And parameters indices should be consistent with the function arity.
I agree with that, but don't see the relevance. In my world, the arity of
int (foo::*)(int)
is 2.
You can look at it this way and I do not doubt it's a useful model for several cases. However, I think it's not very true to the nature of things, because: 1. Noone should put functions into classes arbitrarily. The context reference refers to function's primary working environment which is semantically different from its parameters. 2. As mentioned before, there is a syntactical separation in the invocation. 3. The context reference is often passed in a CPU register instead of the stack. So there is even a technical separation.
Further it makes client code more expressive.
On what do you base that claim?
The separation between parameters and context reference is part of the language so the average programmer will think of a parameter as being something declared within a comma-separated list in parentheses following the function's name.
I guess it's your turn here to prove my design is faulty ;-).
It's not so much faulty as needlessly irregular. I believe that will be an inconvenience in some real applications, and at the very least will cost more template instantiations than necessary.
For maximum efficiency (== minimum number of template instantiations) there is: function_type_signature<T>::types
If you can convince me, I'll happily change things, of course.
How'd I do?
I was trying to say that I'm not pedantic about this issue, but: Rationale: "It's so, because it's Dave's taste" is too weak, IMO ;-). But let's not argue too much on this and suspend this discussion until after the proof - probably I see things differently, then. Like the idea ?
[...]
But the latter doesn't look like a "use" of function_type_signature at all! Anyway, don't clarify your meaning for mehere; propose a documentation fix. Just look very carefully at the words you used (like "use," "primary interface," "white box," etc.), and consider how they might be (mis)interpreted by someone who doesn't already know what you're talking about.
It should state that 'function_type_signature' is used to implement all the other inspection components (it is very important to give the reader this insight, I figure). The latter should be preferred (and thus recommended) in general, because they make client code more readable and provide error checking. The possibility to make direct use the type members of 'function_type_signature' is mainly for optimization and for getting the represented type of a modified sequence. ^^ Not literally for the docs, but it's about what this paragraph should say. Hope no fuzzy terms are left.
[... function_type_signature ]
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.
See above.
Still waiting for a satisfactory answer. Seems to me that your library only gets easier to use (and learn!) if it traffics in one uniform structure.
Oh - looks like our favourite (== only?) disagreement again: Any template, except 'function_type_signature' and 'function_type' should be intuitive and straightforward enough to hardly ever require a reader of client code to look into the documentation of this library. 'function_type_parameter*' reflects its paramter list, 'function_type_signature' reflects its signature (where the definition of what's part of the signature should be in the docs). That hard?
[...]
Well, I suggest you say something that the average dummy is more likely to understand, like, "you better #include <whatever> or this won't compile."
;-). Probably a bit too informal to copy it literally, but I like the direction.
[...]
It used to be a bit more understandable before I removed a rather strange feature to grab the signature from a class template's parameter list.
I find it hard to believe that it was easier to understand when there was an *additional* feature in its behavior!
The other was its counterpart (in a different template)...
I will just remove this as well.
That sounds like it goes in the right direction.
;-)
Also, special-case "if" clauses, especially those that test whether something matches a concept (like "is_sequence") tend to destroy genericity.
Would you mind explaining this in some more detail?
Take boost::variant, which (at one time -- still?) would accept up to N arguments describing the held types, *OR* an MPL sequence of the types. The problem happens when your generic code wants to create a variant of one element that happens to be an MPL sequence type. Now
OK - understood.
you need a special case that sticks that sequence in another sequence. Well, in that case the sequence interface is the only one of any use to you, isn't it?
The point is that switching on a type's properties often introduces nonuniformity of semantics, which hurts genericity. I didn't analyze your case to see if it was a problem here in particular.
Not really, as the properties "MPL-Sequence" and "function type" should be mutually exclusive. Well, you can use any type which is not a function type, but it won't make sense (and will result in a compile error) - if this type happens to be a sequence it will "work" again. This should be acceptable as long as this behaviour is documented, I guess.
- Improve uniformity, if I can get it. I would probably accept the lib without that change, but I fear I will grumble every time I use it.
Well, let's see... We should be careful, here.
- change docs
I hope (but am not entirely sure) it's all doable within the review period. Any hints on a preferred prioritization?
1. Proof it 2. Post updated docs that include the proposed naming changes 3. Change the code.
But I'm not too particular about it. I think the proof should come first because I'd happily accept assurances in place of steps 2 and 3 happening before the review period ends.
Thanks again for your help. Regards, Tobias

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Tobias Schwinger
Only counting "real" parameters here allows us to build such a cascade for arities ranging from zero to "OUR_MAX_ARITY_LIMIT" without having to deal with the two special cases that there are no nullary member functions and that we need to go up to OUR_ARITY_LIMIT+1 for member function invocations if the context reference is taken into account.
Okay, I've dealt with that issue. But it's a (really) minor one. Generating these things with the preprocessor should usually be done with vertical repetition (you'll get lousy compile times and no debuggability otherwise), which makes that sort of iteration bounds adjustment trivial.
This sounds like using two (slow preprocessor-) loops in client code for members and non-members, where using one would be appropriate...
I agree it can be "minor", but I currently fail to see it's "less minor" than your complaint in the first place.
I'm having some trouble understanding the second part of the above paragraph. What could "otherwise" possibly refer to in this context ? And isn't vertical repetition the slowest form of PP repetition there is ? Can you perhaps help me with it ?
Actually, on slow preprocessors vertical repetition is *significantly* faster. The only problem is that you cannot entirely encapsulate it in a single macro. If you show me what you want to generate, I can help you with this. Regards, Paul Mensonides

Paul Mensonides wrote:
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Tobias Schwinger
Only counting "real" parameters here allows us to build
such a cascade
for arities ranging from zero to "OUR_MAX_ARITY_LIMIT"
without having
to deal with the two special cases that there are no nullary member functions and that we need to go up to OUR_ARITY_LIMIT+1 for member function invocations if the context reference is taken into account.
Okay, I've dealt with that issue. But it's a (really) minor one. Generating these things with the preprocessor should
usually be done
with vertical repetition (you'll get lousy compile times and no debuggability otherwise), which makes that sort of iteration bounds adjustment trivial.
This sounds like using two (slow preprocessor-) loops in client code for members and non-members, where using one would be appropriate...
I agree it can be "minor", but I currently fail to see it's "less minor" than your complaint in the first place.
I'm having some trouble understanding the second part of the above paragraph. What could "otherwise" possibly refer to in this context ? And isn't vertical repetition the slowest form of PP repetition there is ? Can you perhaps help me with it ?
Actually, on slow preprocessors vertical repetition is *significantly* faster. The only problem is that you cannot entirely encapsulate it in a single macro.
Interesting (depsite the #include)! I like to hear that, since I use it a lot...
If you show me what you want to generate, I can help you with this.
Well, this was about "what would be helpful in case X"-design-consideration. However, if you have some (perhaps general) tips for me on how to speed up or enhance my preprocessing code it would be great! The BOOST_FT_UNROLL routine at the bottom of this [ http://tinyurl.com/d4964 ] file is the heart of any preprocessing code in the library, in case you want to take a look. I just use and trust your library - most of its inside is still pretty much black magic to me so the code may be naive (just so you're warned)... Thanks for offering your help! Regards, Tobias

Tobias Schwinger <tschwinger@neoscientists.org> writes:
But the latter doesn't look like a "use" of function_type_signature at all! Anyway, don't clarify your meaning for mehere; propose a documentation fix. Just look very carefully at the words you used (like "use," "primary interface," "white box," etc.), and consider how they might be (mis)interpreted by someone who doesn't already know what you're talking about.
It should state that 'function_type_signature' is used to implement all the other inspection components (it is very important to give the reader this insight, I figure).
That's an implementation detail. How could it possibly matter to the reader? -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
But the latter doesn't look like a "use" of function_type_signature at all! Anyway, don't clarify your meaning for mehere; propose a documentation fix. Just look very carefully at the words you used (like "use," "primary interface," "white box," etc.), and consider how they might be (mis)interpreted by someone who doesn't already know what you're talking about.
It should state that 'function_type_signature' is used to implement all the other inspection components (it is very important to give the reader this insight, I figure).
That's an implementation detail. How could it possibly matter to the reader?
It's an implicit explanation for many aspects of this interface. But I agree -- it would be better not to say it in terms of implementation details. Still, it's important to highlight here is some redundancy... I'll give it another try: 'function_type_signature' can be used to achieve similar functionality as provided by the decomposition components. < Maybe need an example, here ? - I have some objections providing examples for non-recommended use, though. > This application, however, is not recommended (*), because there is no implied assertion that its template argument really is a function type and because its type members form a less expressive interface for decomposistion. (*) It can be an opportunity for optimization in heavily repetitive situations to reduce the number of template instantiations required. I'm currently unsure if this is satisfactory. I'll have to re-read it later, not immediately after writing, to at least get a vague idea. Thanks, Tobias

David Abrahams wrote:
[...] 1. Proof it
It has been applied to Boost.Lambda and to boost:mem_fn (which is a part of Bind). I'll attach the results to the end of this post. An archive containing the results, all refactored code and a diff for Boost.Lambda (to easily spot the changes) has been uploaded to the vault: http://tinyurl.com/c3tpv
2. Post updated docs that include the proposed naming changes 3. Change the code.
But I'm not too particular about it. I think the proof should come first because I'd happily accept assurances in place of steps 2 and 3 happening before the review period ends.
Assurances: The docs will be revised. This includes clarification of all the spots that have caused confusion or have been makred as improvable by the reviewers. This also includes inline examples and adding some kind of (a) simple tutorial(s). Const/volatile-qualification of member function pointers will be handled through the class type. The interface will be put in a sub namespace and the names will be shortened. Portability and test suite will be extended. Regards, Tobias --- mem_fn ~/boost $ wc -l mem_fn.hpp mem_fn/data_member_adaptor.hpp mem_fn/functor_preprocessed.hpp 89 mem_fn.hpp 97 mem_fn/data_member_adaptor.hpp 466 mem_fn/functor_preprocessed.hpp (#1) 652 total The file functor.hpp was not counted because it is only used to generate functor_preprocessed.hpp with Boost.Preprocessor. ~/boost $ wc -l mem_fn.bak/* 394 mem_fn.bak/mem_fn.hpp 103 mem_fn.bak/mem_fn_cc.hpp (#2) 934 mem_fn.bak/mem_fn_template.hpp (#2) (#1) 130 mem_fn.bak/mem_fn_vw.hpp (#2) 1561 total (#1) times 2 for BOOST_NO_VOID_RETURNS (#2) times the number of enabled calling conventions --- lambda ~/boost $ wc -l lambda/* lambda/detail/* wc: lambda/CVS: Invalid request code 1377 lambda/algorithm.hpp 19 lambda/bind.hpp 219 lambda/casts.hpp 274 lambda/closures.hpp 237 lambda/construct.hpp 22 lambda/control_structures.hpp 79 lambda/core.hpp 0 lambda/detail 1740 lambda/exceptions.hpp 462 lambda/if.hpp 34 lambda/lambda.hpp 505 lambda/loops.hpp 119 lambda/numeric.hpp 502 lambda/switch.hpp 174 lambda/detail/actions.hpp 110 lambda/detail/arity_code.hpp 1879 lambda/detail/bind_functions.hpp 50 lambda/detail/control_constructs_common.hpp 550 lambda/detail/control_structures_impl.hpp 519 lambda/detail/function_adaptors.hpp 104 lambda/detail/is_instance_of.hpp 48 lambda/detail/lambda_config.hpp 599 lambda/detail/lambda_functor_base.hpp 212 lambda/detail/lambda_functors.hpp 78 lambda/detail/lambda_fwd.hpp 556 lambda/detail/lambda_traits.hpp 358 lambda/detail/member_ptr.hpp 139 lambda/detail/operator_actions.hpp 271 lambda/detail/operator_lambda_func_base.hpp 942 lambda/detail/operator_return_type_traits.hpp 370 lambda/detail/operators.hpp 325 lambda/detail/ret.hpp 287 lambda/detail/return_type_traits.hpp 74 lambda/detail/select_functions.hpp 13234 total ~/boost $ wc -l lambda.bak/* lambda.bak/detail/* wc: lambda.bak/CVS: Invalid request code 1377 lambda.bak/algorithm.hpp 19 lambda.bak/bind.hpp 219 lambda.bak/casts.hpp 274 lambda.bak/closures.hpp 237 lambda.bak/construct.hpp 22 lambda.bak/control_structures.hpp 79 lambda.bak/core.hpp 0 lambda.bak/detail 1740 lambda.bak/exceptions.hpp 462 lambda.bak/if.hpp 34 lambda.bak/lambda.hpp 505 lambda.bak/loops.hpp 119 lambda.bak/numeric.hpp 502 lambda.bak/switch.hpp 174 lambda.bak/detail/actions.hpp 110 lambda.bak/detail/arity_code.hpp 1879 lambda.bak/detail/bind_functions.hpp 50 lambda.bak/detail/control_constructs_common.hpp 550 lambda.bak/detail/control_structures_impl.hpp 640 lambda.bak/detail/function_adaptors.hpp 104 lambda.bak/detail/is_instance_of.hpp 48 lambda.bak/detail/lambda_config.hpp 599 lambda.bak/detail/lambda_functor_base.hpp 212 lambda.bak/detail/lambda_functors.hpp 74 lambda.bak/detail/lambda_fwd.hpp 527 lambda.bak/detail/lambda_traits.hpp 737 lambda.bak/detail/member_ptr.hpp 139 lambda.bak/detail/operator_actions.hpp 271 lambda.bak/detail/operator_lambda_func_base.hpp 942 lambda.bak/detail/operator_return_type_traits.hpp 370 lambda.bak/detail/operators.hpp 325 lambda.bak/detail/ret.hpp 284 lambda.bak/detail/return_type_traits.hpp 74 lambda.bak/detail/select_functions.hpp 13698 total Compilers tested with: MSVC 7.1, GCC 3.4 All Lambda tests pass (Lambda would probably even work for any calling convention if TypeTraits would) Bind tests for mem_fn pass (*) When properly configured all non-standard calling convention tests pass (MSVC only, members can not have custom calling conventions with GCC<4 because of a compiler bug). Bind as a whole does not pass because it accesses implementation details of the old mem_fn version. It was not changed because of the limitied time. (*) mem_fn_eq_test does not pass for MSVC - the code involved looks extremely trivial and compare operators haven't changed - no idea what's up there - not sure it passed before the changes.

Tobias Schwinger <tschwinger@neoscientists.org> writes:
David Abrahams wrote:
[...] 1. Proof it
It has been applied to Boost.Lambda and to boost:mem_fn (which is a part of Bind).
I'll attach the results to the end of this post. An archive containing the results, all refactored code and a diff for Boost.Lambda (to easily spot the changes) has been uploaded to the vault:
Can you say a little something to help me understand the wc numbers you give at the end of your post? -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
David Abrahams wrote:
[...] 1. Proof it
It has been applied to Boost.Lambda and to boost:mem_fn (which is a part of Bind).
I'll attach the results to the end of this post. An archive containing the results, all refactored code and a diff for Boost.Lambda (to easily spot the changes) has been uploaded to the vault:
Can you say a little something to help me understand the wc numbers you give at the end of your post?
Sure. Sorry for the too brief summary. It was late in my timezone and I had to get some sleep. 'wc' is a Unix shell command and stands for "word count" (I'm pretty sure you know that - but some readers might not). With the '-l' it counts the number of lines in (a) file(s). Since you requested to find out the portential of Function Types to eliminate existing/redundant Boost code, the output of the 'wc' command was meant to document ammount of factored out code. --- Function template boost::mem_fn (lives in the directory of Boost.Bind) The original uses files with different versions of templates for arity 0-9 and macros in them. These are repetetively included with different macro definitions to generate variations for different calling conventions and with or without 'return' (if BOOST_NO_VOID_RETURNS is set). The changed version does not need these variations. And because it's tedious too do everything ten times, I used Boost.Preprocessor to help me with it (for the arity part). For counting lines, however, I used an already-preprocessed version of this file (because we want to measure Function Types and not Boost.Preprocessor). Without BOOST_NO_VOID_RETURNS and only the default calling convention we end up with 936 lines less (the original version needs 1561 lines total - my version needs 625 -- actually 325 but this would be "unfair" because of using Boost.Preprocessor). The footnotes indicate how the preprocessed size of the library scales up, when certain features or workarounds are enabled. --- The Function Types library applied to Boost.Lambda: There are 464 lines less in the Lambda library. It can theoretically take advantage of the common configuration - in practice Type Traits still keeps it from working with any calling convention. This file contains the code for handling member pointers: changed 358 lines in lambda/detail/member_ptr.hpp original 737 lines in lambda.bak/detail/member_ptr.hpp This file contains the functor frontend for lambda functors, data members and functions: changed 519 lines in lambda/detail/function_adaptors.hpp original 640 lines in lambda.bak/detail/function_adaptors.hpp A wrapper for 'function_type_class' (to decorate cv-qualification) has been put here. This will not be needed anymore with the next version of Function Types: changed 556 lambda/detail/lambda_traits.hpp original 527 lambda.bak/detail/lambda_traits.hpp See the diff file included in the archive for details (it's too large to post it here). Hope this works. Please ask if something is unclear. Regards, Tobias

This is a kind of first look, review still in progress set of comments:
What is your evaluation of the design?
Overall it looks mostly fine. function_type_class: if the member function is cv-qualified is the result of this metafunction cv-qualified also? If not is there a way to tell if a member function is cv-qualified? Reading the docs, I don't understand what function_type_signature is for, especially when it's members are not recomended for general use. Should this be an implementation detail? function_type: OK I understand what it does, but I'm having a hard time figuring out what it would be for. It's one thing to be able to create a specific function type, but then you have to be able to do something with it ;-)
What is your evaluation of the implementation?
Looks OK as far as I've been able to judge. Some commenbts on the tests: I see lots of tests for is_function_type and very little for the composition and decompostion functions. I'd like to see the same sort of thoughoughness that's gone into arity_and_type.cpp applied to the other templates (probably one test for each).
What is your evaluation of the documentation?
OK as a reference document (modulo Dave Abrahams comments, which should be addressed). The big issue I have is with the motivation section: the problem I have is that the usefullness of the library isn't immediately obvious to me. Some of the worked examples might address this, but the interpreter example to pick just one is quite complicated, so some kind of tutorial is required here. The links to the examples, look more like links to tests to me, it would be much more comprehensible if there were code snippets to illustrate the points you're trying to make directly in the documentation.
What is your evaluation of the potential usefulness of the library?
This is the real problem, I can see how a function's return type and arity may be useful, but I'm having a hard time figuring out what access to the type of specific function arguments is good for, likewise what the mpl-list based composition/decomposition functions are for. Better motivating examples may help, however there is a seems to be substantial overlap between the functionality offered here and boost::bind, boost::function etc. Is this library intended to simplify those libraries? If so what do those libraries authors think?
Did you try to use the library? With what compiler? Did you have any problems?
The compilers I expected to pass the tests did so (VC7.1 and 8, Intel 7.1, 8, and 9, gcc various versions).
Do you think the library should be accepted as a Boost library?
Tricky: yes if there is a clear demand, no otherwise. John.

Hello John, John Maddock wrote:
This is a kind of first look, review still in progress set of comments:
thank you for your first look, then.
What is your evaluation of the design?
Overall it looks mostly fine.
function_type_class: if the member function is cv-qualified is the result of this metafunction cv-qualified also? If not is there a way to tell if a member function is cv-qualified?
Yes. Currently it's: is_function_type<const_member_function_pointer,T>::value However, I will change this. So it will be: is_const< function_type_class<T>::type >::value which is what you propose and way more practical. I use a wrapper to model this in many places and I really want to get rid of it.
Reading the docs, I don't understand what function_type_signature is for, especially when it's members are not recomended for general use. Should this be an implementation detail?
No, but the docs need to be fixed, here. 'function_type_signature' a model of MPL Random Access sequence (even an "extensible" one if the synthesis part has been enabled by #including 'function_type.hpp'). The 'kind' member is the "missing piece of information" to fully describe the encapsulated type (in combination with the subtypes stored in the sequence). The 'representee' member is neccessary to get the type which it (e.g. a modified sequence) describes. Further (what follows is a preliminary try to improve this recommendation): 'function_type_signature' can be used to achieve similar functionality as provided by the decomposition components. This application, however, is not recommended (*), because there is no implied assertion that its template argument really is a function type and because its type members form a less expressive interface for decomposistion. (*) It can be an opportunity for optimization in heavily repetitive situations to reduce the number of template instantiations required or to avoid to depend on the synthesis part of the library if an extensible sequence is needed which does not have to be reassembled to a function type.
function_type: OK I understand what it does, but I'm having a hard time figuring out what it would be for. It's one thing to be able to create a specific function type, but then you have to be able to do something with it ;-)
Like taking the address of an overloaded function or function template instantiation ?? Ref: 13.4 Address of overloaded function 14.8.2.2 Deducing template arguments taking the address of a function template 14.8.2.4 Deducing template arguments from a type We can even declare functions this way, as long as we don't define them [8.3.5-7] - although I can't really imagine a situation where this one makes a lot of sense, right now ;-). Or another one: Let's say we have some function type and want to create a template argument for Boost.Function with an optimal forwarding signature (i.e. use call_traits<T>::param_type to compute the parameter types). For member function pointers we will parametrize the transformation applied to the class type (and set the default to add a reference): // MPL friendly wrapper for call_traits<T>::param_type template<typename T> struct param_type { typedef typename call_traits<T>::param_type type; }; // Metafunction to compute an optimized forwarding signature template < typename FunctionType , typename ClassTypeTransform = add_reference<_1> > struct forward_signature_function_type : function_type < plain_function , typename mpl::copy < function_type_signature<FunctionType> , mpl::inserter < mpl::vector<> , mpl::push_back < _1 , mpl::if_ < mpl::greater < mpl::size<_1> , typename mpl::if_ < is_function_type<member_function_pointer,FunctionType> , mpl::size_t<1> , mpl::size_t<0> >::type > , param_type<_2> , mpl::if_ < mpl::equal_to< mpl::size<_1>, mpl::size_t<1> > , mpl::apply1 < typename mpl::lambda<ClassTypeTransform>::type, _2 > , _2 > > > > >::type > { }; OK, OK - I admit, this implementation is ugly.
What is your evaluation of the implementation?
Looks OK as far as I've been able to judge.
Some commenbts on the tests: I see lots of tests for is_function_type and very little for the composition and decompostion functions. I'd like to see the same sort of thoughoughness that's gone into arity_and_type.cpp applied to the other templates (probably one test for each).
As I plan to extend portability, I won't get around writing more proper regressions. Type synthesis needs better testing most badly. Currently, there is not that much to test in terms of decomposition. Correct arity and tag means 'signature_impl' (the heart of all classification and decomposition) works. Of course I could test every type, but it's really just template specialization and there is little chance for typos in generated code. The "tag logic" is based on binary arithmetic and the part of the encoding that is edited by hand is quite small. Errors in there are caught pretty well by the "example/test hybrids". But they are by no means perfect, yet.
What is your evaluation of the documentation?
OK as a reference document (modulo Dave Abrahams comments, which should be addressed).
The big issue I have is with the motivation section: the problem I have is that the usefullness of the library isn't immediately obvious to me. Some of the worked examples might address this, but the interpreter example to pick just one is quite complicated, so some kind of tutorial is required here.
The links to the examples, look more like links to tests to me, it would be much more comprehensible if there were code snippets to illustrate the points you're trying to make directly in the documentation.
I agree. In fact, I'm already collecting more illustrative code snippets.
What is your evaluation of the potential usefulness of the library?
This is the real problem, I can see how a function's return type and arity may be useful, but I'm having a hard time figuring out what access to the type of specific function arguments is good for,
OK. Let's start with the conservative side of the story: Of course we can always spell things out like this: template<typename R> void do_something_with_function(R (*)()); template<typename R,typename A1> void do_something_with_function(R (*)(A1)); template<typename R,typename A1,typename A2> void do_something_with_function(R (*)(A1,A2)); //... And in this case we know all paramter types, of course. When doing complex things we end up writing several of these cascades (as we usually don't get all work done in one function), writing code generators to help us with it, add configuration options for __stdcall, __fastcall, etc. or forget them. The neighbouring library does the same thing. If we can completely decompose function types we won't need to do this. We can just use a simple template function instead: template<typename FuncPtr> void do_something_with_function(FuncPtr f) { // maybe asserts f really is a function pointer } Well, we won't get rid of repetitive parts entirely, because of the invocation. However, we can reduce them. This code reduction is especially significant when we are supporting cv-qualified member function pointers or several different calling conventions. We don't have to deal with this stuff anymore at all because the invocation doesn't care. The decomposition facility handles it for us. But this it's not only about reducing thousand lines of code, supporting exotic things like variadic functions or non-standard calling conventions and a common point of configuration for it - it's about software design: making design choices is much easier knowing there is a choice, so the existence of library is going to influence future design decisions. Nice words to shift over to some more progressive things like (what I call) "intelligent" callback facilities: The basic idea is to generate functionality that feeds a function with arguments based on its signature. The interpreter example tries to illustrate this by providing a limited command line interpreter that allows to call previously registered C++ functions and simple functors (lexical_cast is applied to input from a token_iterator to feed the function). Taking this idea a bit further, making it e.g. a spirit parser with parametrized (per-parameter-type) argument parsing, also handling the result, would make it a matter of minutes to layer a functional scripting languages over existing C++ code. An event processing framwork could use the same technique to hand event handler functions the data they are interested in, for example.
likewise what the mpl-list based composition/decomposition functions are for.
E.g: o Setting up a table of function signatures and taking the address of a function overloaded this way by index. o Concept checking (does my function take "key_t, value_t" n-times ?). o Doing transformations (like the weird code above does - I guess there are better examples out there). o Writing a traits class to probe the number of default arguments of a function with a particular name (operator() would be a good one, I guess).
Better motivating examples may help, however there is a seems to be substantial overlap between the functionality offered here and boost::bind, boost::function etc. Is this library intended to simplify those libraries?
Not exclusively. There is potential to improve existing Boost code, already. But this library must become more portable to be applied universally. Not much to do for Function, since it doesn't really do a lot with the types (it either uses MemFn or a cast to void*). Once the portability matches, three lines (or so) can be changed to use this library for classification (it'll work for __stdcall and friends then, given FunctionTypes is configured to support them). Lambda and Phoenix will benefit from using this library - and the ResultOf utiltiy too, of course. I'm not sure on Bind as a whole, yet. boost::mem_fn (which seems to be a part of Bind) is a candidate, though.
If so what do those libraries authors think?
I'ld really love to know! In fact, AFAIK you are one of them (maintainer, at least) as there is quite some overlap with TypeTraits. However, FunctionTypes is not portable enough for this, yet. And when it is, it will require a lot of precision not to end up with circular dependencies. Currently only remove_cv is used directly so there is a chance it's doable... Regards, Tobias

Yes. Currently it's:
is_function_type<const_member_function_pointer,T>::value
However, I will change this. So it will be:
is_const< function_type_class<T>::type >::value
which is what you propose and way more practical. I use a wrapper to model this in many places and I really want to get rid of it.
OK that looks cleaner.
Reading the docs, I don't understand what function_type_signature is for, especially when it's members are not recomended for general use. Should this be an implementation detail?
No, but the docs need to be fixed, here.
'function_type_signature' a model of MPL Random Access sequence (even an "extensible" one if the synthesis part has been enabled by #including 'function_type.hpp').
The 'kind' member is the "missing piece of information" to fully describe the encapsulated type (in combination with the subtypes stored in the sequence).
The 'representee' member is neccessary to get the type which it (e.g. a modified sequence) describes.
Further (what follows is a preliminary try to improve this recommendation):
'function_type_signature' can be used to achieve similar functionality as provided by the decomposition components.
This application, however, is not recommended (*), because there is no implied assertion that its template argument really is a function type and because its type members form a less expressive interface for decomposistion.
(*) It can be an opportunity for optimization in heavily repetitive situations to reduce the number of template instantiations required or to avoid to depend on the synthesis part of the library if an extensible sequence is needed which does not have to be reassembled to a function type.
Understood.
function_type: OK I understand what it does, but I'm having a hard time figuring out what it would be for. It's one thing to be able to create a specific function type, but then you have to be able to do something with it ;-)
Like taking the address of an overloaded function or function template instantiation ??
Surely you can always just declare your function type as a typedef in that case, without going through function_type? function_type is only useful in that it unpacks an mpl sequence defining the argument list into the function type, but that's only useful if you have an the mpl sequence to begin with. It's those situations I'm finding hard to imagine at present.
Or another one:
Let's say we have some function type and want to create a template argument for Boost.Function with an optimal forwarding signature (i.e. use call_traits<T>::param_type to compute the parameter types). For member function pointers we will parametrize the transformation applied to the class type (and set the default to add a reference):
OK, you mean take a function sig, decompose to mpl sequent, transform the sequence, and recompose again? I think I can understand that, but doesn't this get complicated: presumably the transform should be applied only to the arguments and not the return type and class type (if present) which complicates the code no end unless I'm mistaken.
// MPL friendly wrapper for call_traits<T>::param_type template<typename T> struct param_type { typedef typename call_traits<T>::param_type type; };
// Metafunction to compute an optimized forwarding signature template < typename FunctionType , typename ClassTypeTransform = add_reference<_1> > struct forward_signature_function_type : function_type < plain_function , typename mpl::copy < function_type_signature<FunctionType> , mpl::inserter < mpl::vector<> , mpl::push_back < _1 , mpl::if_ < mpl::greater < mpl::size<_1> , typename mpl::if_ < is_function_type<member_function_pointer,FunctionType> , mpl::size_t<1> , mpl::size_t<0> >::type > , param_type<_2> , mpl::if_ < mpl::equal_to< mpl::size<_1>, mpl::size_t<1> > , mpl::apply1 < typename mpl::lambda<ClassTypeTransform>::type, _2 > , _2 > > > > >::type > { };
OK, OK - I admit, this implementation is ugly.
It's hard to grok. It's basically a whole mpl tutorial in it's own right ;-)
What is your evaluation of the implementation?
Looks OK as far as I've been able to judge.
Some commenbts on the tests: I see lots of tests for is_function_type and very little for the composition and decompostion functions. I'd like to see the same sort of thoughoughness that's gone into arity_and_type.cpp applied to the other templates (probably one test for each).
As I plan to extend portability, I won't get around writing more proper regressions.
Type synthesis needs better testing most badly.
Currently, there is not that much to test in terms of decomposition. Correct arity and tag means 'signature_impl' (the heart of all classification and decomposition) works. Of course I could test every type, but it's really just template specialization and there is little chance for typos in generated code.
The "tag logic" is based on binary arithmetic and the part of the encoding that is edited by hand is quite small. Errors in there are caught pretty well by the "example/test hybrids". But they are by no means perfect, yet.
Maybe, but my experience with type traits is that: "If the tests all pass, then you haven't written enough of them yet." Of course a lot of the failures that show up are compiler problems :-(
I agree. In fact, I'm already collecting more illustrative code snippets.
Yes, I spotted that, looks like you've got it in hand.
What is your evaluation of the potential usefulness of the library?
This is the real problem, I can see how a function's return type and arity may be useful, but I'm having a hard time figuring out what access to the type of specific function arguments is good for,
OK. Let's start with the conservative side of the story:
Of course we can always spell things out like this:
template<typename R> void do_something_with_function(R (*)()); template<typename R,typename A1> void do_something_with_function(R (*)(A1)); template<typename R,typename A1,typename A2> void do_something_with_function(R (*)(A1,A2)); //...
And in this case we know all paramter types, of course.
When doing complex things we end up writing several of these cascades (as we usually don't get all work done in one function), writing code generators to help us with it, add configuration options for __stdcall, __fastcall, etc. or forget them. The neighbouring library does the same thing.
What's "neighbouring library"?
If we can completely decompose function types we won't need to do this. We can just use a simple template function instead:
template<typename FuncPtr> void do_something_with_function(FuncPtr f) { // maybe asserts f really is a function pointer }
Well, we won't get rid of repetitive parts entirely, because of the invocation.
However, we can reduce them. This code reduction is especially significant when we are supporting cv-qualified member function pointers or several different calling conventions. We don't have to deal with this stuff anymore at all because the invocation doesn't care. The decomposition facility handles it for us.
I can see that argument for decomposing the arity, I'm less sure about the others, I've got no imagination I guess ;-)
But this it's not only about reducing thousand lines of code, supporting exotic things like variadic functions or non-standard calling conventions and a common point of configuration for it - it's about software design: making design choices is much easier knowing there is a choice, so the existence of library is going to influence future design decisions.
OK.
Nice words to shift over to some more progressive things like (what I call) "intelligent" callback facilities: The basic idea is to generate functionality that feeds a function with arguments based on its signature.
The interpreter example tries to illustrate this by providing a limited command line interpreter that allows to call previously registered C++ functions and simple functors (lexical_cast is applied to input from a token_iterator to feed the function). Taking this idea a bit further, making it e.g. a spirit parser with parametrized (per-parameter-type) argument parsing, also handling the result, would make it a matter of minutes to layer a functional scripting languages over existing C++ code.
An event processing framwork could use the same technique to hand event handler functions the data they are interested in, for example.
OK.
likewise what the mpl-list based composition/decomposition functions are for.
E.g:
o Setting up a table of function signatures and taking the address of a function overloaded this way by index.
o Concept checking (does my function take "key_t, value_t" n-times ?).
o Doing transformations (like the weird code above does - I guess there are better examples out there).
o Writing a traits class to probe the number of default arguments of a function with a particular name (operator() would be a good one, I guess).
Better motivating examples may help, however there is a seems to be substantial overlap between the functionality offered here and boost::bind, boost::function etc. Is this library intended to simplify those libraries?
Not exclusively.
There is potential to improve existing Boost code, already. But this library must become more portable to be applied universally.
Not much to do for Function, since it doesn't really do a lot with the types (it either uses MemFn or a cast to void*). Once the portability matches, three lines (or so) can be changed to use this library for classification (it'll work for __stdcall and friends then, given FunctionTypes is configured to support them).
Lambda and Phoenix will benefit from using this library - and the ResultOf utiltiy too, of course.
I'm not sure on Bind as a whole, yet. boost::mem_fn (which seems to be a part of Bind) is a candidate, though.
If so what do those libraries authors think?
I'ld really love to know!
In fact, AFAIK you are one of them (maintainer, at least) as there is quite some overlap with TypeTraits. However, FunctionTypes is not portable enough for this, yet. And when it is, it will require a lot of precision not to end up with circular dependencies. Currently only remove_cv is used directly so there is a chance it's doable...
The main overlap is with function_traits I guess: that was always a bit of a stop gap so that function and signals could just get the job done. I'm not sure how the overlap with is_function/is_member_function_pointer goes, and/or which implementation is the more "lightweight" (probably not much in it), if function_traits could produce versions of these traits that passed all the existing tests then that would be a big portability thumbs up for function_types. I've certainly no objection to recasting those traits in terms of function_types if it simplifies things. One thing I've been meaning to look into, but haven't had the time yet: how do you handle __stdcall/__fastcall etc? Last time I tried to write partial specialisations that would select function types with specific calling conventions I couldn't get it to work. There's also the horrible __thiscall thing: you can't explicitly declare a member function pointer type with __thiscall, but it is usually (but not always) the default, so whether or not: void (myclass::*)(); and void (__cdecl myclass::*)(); are the same type or not, depends upon the command line arguments to VC, and you can't tell what those options are at compile time as far as I know. John.

"John Maddock" <john@johnmaddock.co.uk> writes:
Like taking the address of an overloaded function or function template instantiation ??
Surely you can always just declare your function type as a typedef in that case, without going through function_type?
function_type is only useful in that it unpacks an mpl sequence defining the argument list into the function type, but that's only useful if you have an the mpl sequence to begin with. It's those situations I'm finding hard to imagine at present.
I have a use for it -- but it's less useful if the sequence is nonuniform and fails to include the class type of member functions ;-) -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"John Maddock" <john@johnmaddock.co.uk> writes:
Like taking the address of an overloaded function or function template instantiation ??
Surely you can always just declare your function type as a typedef in that case, without going through function_type?
function_type is only useful in that it unpacks an mpl sequence defining the argument list into the function type, but that's only useful if you have an the mpl sequence to begin with. It's those situations I'm finding hard to imagine at present.
I have a use for it -- but it's less useful if the sequence is nonuniform and fails to include the class type of member functions ;-)
In fact, at this very point (type synthesis - "function_type") the input sequence does include it ;-).

I have a use for it -- but it's less useful if the sequence is nonuniform and fails to include the class type of member functions ;-)
In fact, at this very point (type synthesis - "function_type") the input sequence does include it ;-).
Right but decomposition does not? Whatever it's behaviour it should be symmetrical, if composition has the class name in the list, then decomposition should have it there as well. John.

John Maddock wrote:
I have a use for it -- but it's less useful if the sequence is nonuniform and fails to include the class type of member functions ;-)
In fact, at this very point (type synthesis - "function_type") the input sequence does include it ;-).
Right but decomposition does not? Whatever it's behaviour it should be symmetrical, if composition has the class name in the list, then decomposition should have it there as well.
'function_type' and 'function_type_signature' are symmetrical counterparts (and work/are sequences containing all subtypes involved). The templates in the "Decomposition" section of the docs, however, provide access to particular subtypes, such as the result, the class or those that make up the parameter list. Dave's point is (I hope I summarize this accurately, please correct me if not) to put the class type into the sequence of parameters adding (cv-qualifications - but we all agree on this - and) a reference to it. I, however, am not convinced this is an improvement: While this may unify things in some cases it makes things more difficult in others (considering the case where we want a "this pointer" and not a reference, for example). I'll get back to this, later today. Regards, Tobias

Tobias Schwinger <tschwinger@neoscientists.org> writes:
Dave's point is (I hope I summarize this accurately, please correct me if not) to put the class type into the sequence of parameters adding (cv-qualifications - but we all agree on this
I must've missed when that happened
- and) a reference to it.
I think so.
I, however, am not convinced this is an improvement: While this may unify things in some cases it makes things more difficult in others (considering the case where we want a "this pointer" and not a reference, for example).
I can only explain the reasons I chose a reference and not a pointer in the context of Boost.Python: pointers can be null, but references cannot, and you can never call a member function on a null pointer. In Python, the closest thing to a null pointer is called None, and Boost.Python allows None to be converted into a null pointer of any type when calling from Python. You can add methods to the Python view of a wrapped C++ class' interface by wrapping free functions. When setting up a free function to be bound into a wrapped C++ class X, you give it a first parameter of X& so that the type system will only allow the function to be entered with legitimate X instances. Incidentally, we use X& and not X const& even when it's a const member function because a. Python only has a notion of immutability, not constness. b. An argument of type of X const& uses an "rvalue converter" that admits one layer of implicit conversions before the function is called. By contrast, the lifetime of Python object within which an X& is matched will always persist past the call. Many of these considerations are very specific to Boost.Python, obviously. You'd have to think about the most appropriate generalization. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Hi Dave, thanks already for your use case description (it would've been the next thing for me to ask for). I'll get back to this in more detail, later. Just a quick note to ensure we understand each other correctly: David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
Dave's point is (I hope I summarize this accurately, please correct me if not) to put the class type into the sequence of parameters adding (cv-qualifications - but we all agree on this
I must've missed when that happened
"we all agree on this" refers to cv-qualifying the class type, here. Regards, Tobias

Hi Dave, David Abrahams wrote:
I can only explain the reasons I chose a reference and not a pointer in the context of Boost.Python: pointers can be null, but references cannot, and you can never call a member function on a null pointer.
We have to start a "bit further above" ;-). If we want to unify parameters and class type we have to either use a reference or a pointer to it for things to make sense. The by-value case (operator() member of a simple functor) is too unusual to make sense in general. I also agree, that a reference is the more reasonable default. Further, it is attractive to have a reference (as opposed to a value) in the sequence because (e.g): // Let t be a variable of type T and some // T be some (member) function pointer/reference type function< typename function_type<plain_function,T>::type > x = t; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // "rebinds" the subtypes of T to a plain_function will just work. I still don't think it is good to hard-wire this, though. If I understand your case correctly, you want remove const qualification of the class type (which are not there, currently - but planned): typename add_reference < typename remove_cv < typename remove_reference < typename function_type_class<T>::type // or typename at_c<sig,1>::type >::type >::type >::type Slightly better for those who want a pointer: typename add_pointer < typename remove_reference < typename function_type_class<T>::type // or typename at_c<sig,1>::type >::type >::type So I believe it is perhaps best to parametrize the decoartion we want on the class type: Sidenote: I'm not entirely sure on the MPL-Lamda expression - we could also use some kind of tag type or an enum, I guess. Or we use MPL-Lambda exressions, but only evaluate them as a fallback when there is no optimized specialization, as for add_reference<_>, add_pointer<_> and identity<_> (maybe even one to create an unqualified reference for the Boost.Python case). Thoughts welcome. template<typename T, typename ClassDecoration = add_reference<_> > struct function_type_signature; template<typename T, typename ClassDecoration = add_reference<_> > struct function_type_class; And if we want to add a unified one: template<typename T, typename ClassDecoartion = add_reference<_> > struct function_type_effective_parameters; (Jonathan Turkanis has suggested adding one like this. He also came up with "effective" for the name, which I happen to like). And perhaps even: template<typename Tag, typename TypeOrTypes, typename ClassDecoartion = add_reference<_> > struct function_type; // ignores it, unless it specializes function_type_signature Revisiting the example from above we could say: function< typename function_type<plain_function,T,add_pointer<_> >::type > And easily even use our favourite proxy, e.g. a smart pointer... Hoping to get a clear image on how the optimal synopsis of the library has to look like, I'm looking forward to read your comments. Thanks, Tobias

Tobias Schwinger <tschwinger@neoscientists.org> writes:
Hi Dave,
David Abrahams wrote:
I can only explain the reasons I chose a reference and not a pointer in the context of Boost.Python: pointers can be null, but references cannot, and you can never call a member function on a null pointer.
We have to start a "bit further above" ;-).
I don't know what you mean.
If we want to unify parameters and class type we have to either use a reference or a pointer to it for things to make sense.
Yes.
The by-value case (operator() member of a simple functor) is too unusual to make sense in general.
Not sure what you mean here. I don't think there's any case in which a member function can reasonably have the target object passed by value.
I also agree, that a reference is the more reasonable default.
Further, it is attractive to have a reference (as opposed to a value) in the sequence because (e.g):
// Let t be a variable of type T and some // T be some (member) function pointer/reference type function< typename function_type<plain_function,T>::type > x = t; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // "rebinds" the subtypes of T to a plain_function
will just work.
Right.
I still don't think it is good to hard-wire this, though.
Why?
If I understand your case correctly, you want remove const qualification of the class type (which are not there, currently - but planned):
typename add_reference < typename remove_cv < typename remove_reference < typename function_type_class<T>::type // or typename at_c<sig,1>::type >::type >::type >::type
Yes, but I don't really think this library should make any special allowances for my weird case. Boost.Python may not be able to use this library anyway because of the need for vc6/7 compatibility, but if it can, I'll strip the cv-qualification myself.
Slightly better for those who want a pointer:
typename add_pointer < typename remove_reference < typename function_type_class<T>::type // or typename at_c<sig,1>::type >::type >::type
So I believe it is perhaps best to parametrize the decoartion we want on the class type:
I disagree. Too much parameterization is an impediment to usability. [BTW, so is the endline layout you're using for nested templates. There's little precedent for it anywhere that I've seen. You wouldn't write foo ( bar ( baz(1) ) ); would you?]
Sidenote: I'm not entirely sure on the MPL-Lamda expression
Your posting didn't show one above, although I can guess what the example you would have written would look like.
- we could also use some kind of tag type or an enum, I guess. Or we use MPL-Lambda exressions, but only evaluate them as a fallback when there is no optimized specialization, as for add_reference<_>, add_pointer<_> and identity<_> (maybe even one to create an unqualified reference for the Boost.Python case). Thoughts welcome.
template<typename T, typename ClassDecoration = add_reference<_> > struct function_type_signature;
template<typename T, typename ClassDecoration = add_reference<_> > struct function_type_class;
And if we want to add a unified one:
template<typename T, typename ClassDecoartion = add_reference<_> > struct function_type_effective_parameters;
(Jonathan Turkanis has suggested adding one like this. He also came up with "effective" for the name, which I happen to like).
Remind me again why you're still using the function_type_ prefix?
And perhaps even:
template<typename Tag, typename TypeOrTypes, typename ClassDecoartion = add_reference<_> > struct function_type; // ignores it, unless it specializes function_type_signature ^^ ^^ what are these? Are they the same "it?"
Revisiting the example from above we could say:
function< typename function_type<plain_function,T,add_pointer<_> >::type >
And easily even use our favourite proxy, e.g. a smart pointer...
Now that you bring that case up, parameterization starts to look a bit more attractive. I'm still leary of going in that direction, though. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
Hi Dave,
David Abrahams wrote:
I can only explain the reasons I chose a reference and not a pointer in the context of Boost.Python: pointers can be null, but references cannot, and you can never call a member function on a null pointer.
We have to start a "bit further above" ;-).
I don't know what you mean.
It was referring to the next sentence. "Rewinding" the discussion to one thought before the above part of your post...
If we want to unify parameters and class type we have to either use a reference or a pointer to it for things to make sense.
Yes.
The by-value case (operator() member of a simple functor) is too unusual to make sense in general.
Not sure what you mean here. I don't think there's any case in which a member function can reasonably have the target object passed by value.
I'm not entirely sure there is, either. We agree enough, here.
I also agree, that a reference is the more reasonable default.
Further, it is attractive to have a reference (as opposed to a value) in the sequence because (e.g):
// Let t be a variable of type T and some // T be some (member) function pointer/reference type function< typename function_type<plain_function,T>::type > x = t; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // "rebinds" the subtypes of T to a plain_function
will just work.
Right.
I still don't think it is good to hard-wire this, though.
Why?
If I understand your case correctly, you want remove const qualification of the class type (which are not there, currently - but planned):
typename add_reference < typename remove_cv < typename remove_reference < typename function_type_class<T>::type // or typename at_c<sig,1>::type >::type >::type >::type
Yes, but I don't really think this library should make any special allowances for my weird case. Boost.Python may not be able to use this library anyway because of the need for vc6/7 compatibility, but if it can, I'll strip the cv-qualification myself.
The next is not /that/ unusual (at least I've seen it in several places in Boost)...
Slightly better for those who want a pointer:
typename add_pointer < typename remove_reference < typename function_type_class<T>::type // or typename at_c<sig,1>::type >::type >::type
So I believe it is perhaps best to parametrize the decoartion we want on the class type:
I disagree. Too much parameterization is an impediment to usability.
This is only a good argument if no parametrization provides better usability, I guess ;-). And we'll set reference decoration as the default (see further below) so you only see it if we want something else.
[BTW, so is the endline layout you're using for nested templates. There's little precedent for it anywhere that I've seen. You wouldn't write
foo ( bar ( baz(1) ) );
would you?]
Did you mean: __attribute__(__whatever__) foooooooooooooooooooooo ( __attribute__(__whatever__) baaaaaaaaaaaaaaaaaaaaaaar ( __attribute__(__whatever__) baaaaaaaaaaaaaaaaaaaaaaaaaz ).get_something() ).get_something() ;-) ? I sure would!
Sidenote: I'm not entirely sure on the MPL-Lamda expression
^ in the following section
Your posting didn't show one above, although I can guess what the example you would have written would look like.
- we could also use some kind of tag type or an enum, I guess. Or we use MPL-Lambda exressions, but only evaluate them as a fallback when there is no optimized specialization, as for add_reference<_>, add_pointer<_> and identity<_> (maybe even one to create an unqualified reference for the Boost.Python case). Thoughts welcome.
template<typename T, typename ClassDecoration = add_reference<_> > struct function_type_signature;
template<typename T, typename ClassDecoration = add_reference<_> > struct function_type_class;
And if we want to add a unified one:
template<typename T, typename ClassDecoartion = add_reference<_> > struct function_type_effective_parameters;
(Jonathan Turkanis has suggested adding one like this. He also came up with "effective" for the name, which I happen to like).
Remind me again why you're still using the function_type_ prefix?
Because it hasn't been changed in documentation and the library, yet (so it's easier to follow our discussion) and 'class' conflicts with a keyword.
And perhaps even:
template<typename Tag, typename TypeOrTypes, typename ClassDecoartion = add_reference<_> > struct function_type; // ignores it, unless it specializes function_type_signature
^^ ^^ what are these? Are they the same "it?"
Whoops, sorry! I had to re-read it three times to make any sense of it, myself. And on second though the semantics I meant are no good. Please, just ignore it.
Revisiting the example from above we could say:
function< typename function_type<plain_function,T,add_pointer<_> >::type >
And easily even use our favourite proxy, e.g. a smart pointer...
Now that you bring that case up, parameterization starts to look a bit more attractive. I'm still leary of going in that direction, though.
Did you think about iterating the sequences... Thanks, Tobias

Tobias Schwinger <tschwinger@neoscientists.org> writes:
Now that you bring that case up, parameterization starts to look a bit more attractive. I'm still leary of going in that direction, though.
Did you think about iterating the sequences...
Of course. I don't see the relevance. Anyway, I'm starting to think the parameterization is OK. No strong opinions here. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
VVVV ----
Sidenote: I'm not entirely sure on the MPL-Lamda expression
Your posting didn't show one above, although I can guess what the example you would have written would look like.
?! What guess, what example ?!
- we could also use some kind of tag type or an enum, I guess. Or we use MPL-Lambda exressions, but only evaluate them as a fallback when there is no optimized specialization, as for add_reference<_>, add_pointer<_> and identity<_> (maybe even one to create an unqualified reference for the Boost.Python case). Thoughts welcome.
^^^^ ---- This note refers to the following block of code (not obvious enough in my previous reply). Regards, Tobias

John Maddock wrote:
Like taking the address of an overloaded function or function template instantiation ??
Surely you can always just declare your function type as a typedef in that case, without going through function_type?
Yeah, but only for fixed arity. It's not neccessarily the case in every context: template<class F,class T> action make_action(F functor, T& tuple) { // takes address of templated and/or overloaded F::operator() }
function_type is only useful in that it unpacks an mpl sequence defining the argument list into the function type, but that's only useful if you have an the mpl sequence to begin with. It's those situations I'm finding hard to imagine at present.
AFAIK, there is a tuples library which provides interoperability with MPL. However, that's not the primary design consideration: These sequences provide data structures to exchange differently sized, ordered collections of types - and this makes the interface very flexible: Let's say we prefer an interface like this: 'function_type_consideration1< Tag, Result, A0, A1, A2 ... An>'. We can easily model it: 'function_type< Tag, mpl::vector<Result,A0,A1,A2...An> >' Not let's consider we prefer a different one: 'function_type_consideration2< Tag, Result, ParamsSeq >' We can model this as well: 'function_type< Tag, mpl::push_front<ParamSeq,Result> >' or 'function_type< Tag, mpl::joint_view< vector2<Result,Class>, ParamSeq >' ...
OK, you mean take a function sig, decompose to mpl sequent, transform the sequence, and recompose again? I think I can understand that, but doesn't this get complicated: presumably the transform should be applied only to the arguments and not the return type and class type (if present) which complicates the code no end unless I'm mistaken.
For parameters only there is 'function_type_parameters<T>' ....
[... clumsy code ]
^^^ ... and I *really* should've used it here.
OK, OK - I admit, this implementation is ugly.
It's hard to grok. It's basically a whole mpl tutorial in it's own right ;-)
The only problem is: it's so inefficient, that it shows how *not* to do it... Let's try to use separate data sources, reduce the complexity and the number of mpl components needed: ( Sorry, I couldn't resist, the previous one was too bad to leave it alone ;-) ) template < typename FunctionType , typename ClassTypeTransform = add_reference<_1> > struct forward_signature : function_type < plain_function , mpl::joint_view < typename mpl::apply1 < mpl::if_ < is_function_type<member_function_pointer,_1> , mpl::vector2 < function_type_result<_1> , mpl::apply1 < typename mpl::lambda<ClassTypeTransform>::type , function_type_class<_1> > > , mpl::vector1< function_type_result<_1> > > , FunctionType >::type , mpl::transform_view < typename function_type_parameters<FunctionType>::type , param_type<_> > > > { }; Less ugly - but still too heavy practical use. Replacing the first lambda expressions with hard-wired code seems to be the way to go.
I can see that argument for decomposing the arity, I'm less sure about the others, I've got no imagination I guess ;-)
Let's try a simple example: Writing a wrapper functor like 'boost::mem_fn' on top of this library we'ld use template<typename MemFnPtr> mem_fn_functor<MemFnPtr> mem_fn(MemFnPtr member_function_pointer); to let the library handle all the cv, cdecl, ellipsis stuff for us. This way deduction doesn't hand us all parameters. We'll need them to build up the operator() member in the functor, though.
The main overlap is with function_traits I guess: that was always a bit of a stop gap so that function and signals could just get the job done.
I'm not sure how the overlap with is_function/is_member_function_pointer goes, and/or which implementation is the more "lightweight" (probably not much in it), if function_traits could produce versions of these traits that passed all the existing tests then that would be a big portability thumbs up for function_types. I've certainly no objection to recasting those traits in terms of function_types if it simplifies things.
Great. As said before, FunctionTypes is not ripe for this, yet. But I'ld like to take it there...
One thing I've been meaning to look into, but haven't had the time yet: how do you handle __stdcall/__fastcall etc?
There is a configuration table that defines which ones to use (see below for an example).
Last time I tried to write partial specialisations that would select function types with specific calling
Because of ODR ? Simple: in case custom calling conventions are configured, the default one is assumed (the first row in the table) to be among them (so there is no specialization without explicit attributation, then).
conventions I couldn't get it to work. There's also the horrible __thiscall thing: you can't explicitly declare a member function pointer type with __thiscall, but it is usually (but not always) the default, so whether or
Even worse: int(X::*)(int) <=> int( "__thiscall" X::*)(int) int(X::*)(...) <=> int( __cdecl X::*)(...) This is why you can configure, whether a calling convention should support variadic functions or not. So a suitable configuration would be to have an "explicit" thiscall calling convention with an empty attributation modifier, not allowing variadic functions. [ ATTENTION: Not a valid configuration (see below) ] #define BOOST_FT_CALLING_CONVENTIONS \ /*---------------------------------------*/ \ /* name | modifier \ allows '...' */ \ /*------------|------------\-------------*/ \ /* [ non-member or static function ] */ \ ( (( defaultcall, - , 1 )) \ /* [ member function pointers ] */ \ , (( defaultcall, - , 1 )) \ (( thiscall , , 0 )) \ (( cdecl , __cdecl , 1 )) ) The only problem left, is that the preprocessing code will need a minor adjustment, so a cell can be marked as explicitly empty (the table above can't be safely handled by the preprocessor)... [ Confusion protection: The '-' in the first row just indicates a cell which is never evaluated, because the first row has a special meaning to represent the default calling convention. It has nothing to do with what I am talking about, here. ]
not:
void (myclass::*)(); and void (__cdecl myclass::*)();
are the same type or not, depends upon the command line arguments to VC, and you can't tell what those options are at compile time as far as I know.
...and (this one won't go away unless Microsoft implements __thiscall in their frontend, too) that we may have to reconfigure the library when messing with the default calling convention. Setting the default to '__cdecl' in this particular would cause an ODR violation. Thanks, Tobias

Surely you can always just declare your function type as a typedef in that case, without going through function_type?
Yeah, but only for fixed arity. It's not neccessarily the case in every context:
template<class F,class T> action make_action(F functor, T& tuple) { // takes address of templated and/or overloaded F::operator() }
OK I can see that.
For parameters only there is 'function_type_parameters<T>' ....
Missed that, thanks.
Let's try to use separate data sources, reduce the complexity and the number of mpl components needed:
( Sorry, I couldn't resist, the previous one was too bad to leave it alone ;-) )
template < typename FunctionType , typename ClassTypeTransform = add_reference<_1> > struct forward_signature : function_type < plain_function , mpl::joint_view < typename mpl::apply1 < mpl::if_ < is_function_type<member_function_pointer,_1> , mpl::vector2 < function_type_result<_1> , mpl::apply1 < typename mpl::lambda<ClassTypeTransform>::type , function_type_class<_1> > > , mpl::vector1< function_type_result<_1> > > , FunctionType >::type , mpl::transform_view < typename function_type_parameters<FunctionType>::type , param_type<_> > > > { };
OK, that one is more comprehensible now :-)
I can see that argument for decomposing the arity, I'm less sure about the others, I've got no imagination I guess ;-)
Let's try a simple example:
Writing a wrapper functor like 'boost::mem_fn' on top of this library we'ld use
template<typename MemFnPtr> mem_fn_functor<MemFnPtr> mem_fn(MemFnPtr member_function_pointer);
to let the library handle all the cv, cdecl, ellipsis stuff for us. This way deduction doesn't hand us all parameters. We'll need them to build up the operator() member in the functor, though.
Right, it's how you build up the operator() that I'm stuck on, I can see how you can generate the required signature, and you can use that to *declare* the right operator, but how do you then implement the body of the function when you don't know how many parameters there are? Sorry if I'm being dense!
One thing I've been meaning to look into, but haven't had the time yet: how do you handle __stdcall/__fastcall etc?
There is a configuration table that defines which ones to use (see below for an example).
Last time I tried to write partial specialisations that would select function types with specific calling
Because of ODR ?
No just couldn't get it to work reliably, maybe I didn't try hard enough, I was in a rush at the time...
Simple: in case custom calling conventions are configured, the default one is assumed (the first row in the table) to be among them (so there is no specialization without explicit attributation, then).
I saw the mention in the docs, but I suspect most users won't be familiar with the preprocessing library and how to set this up. An example would help a great deal (I suspect most folks will just cut and paste the example in any case). In fact if the code is robust enough (no idea if it is as I haven't had a chance to test it), I would favour enabling support for all the VC calling conventions by default for that compiler, and adding a separate preprocessed header for that one as well.
conventions I couldn't get it to work. There's also the horrible __thiscall thing: you can't explicitly declare a member function pointer type with __thiscall, but it is usually (but not always) the default, so whether or
Even worse:
int(X::*)(int) <=> int( "__thiscall" X::*)(int) int(X::*)(...) <=> int( __cdecl X::*)(...)
This is why you can configure, whether a calling convention should support variadic functions or not.
Yep, good.
So a suitable configuration would be to have an "explicit" thiscall calling convention with an empty attributation modifier, not allowing variadic functions.
[ ATTENTION: Not a valid configuration (see below) ]
#define BOOST_FT_CALLING_CONVENTIONS \ /*---------------------------------------*/ \ /* name | modifier \ allows '...' */ \ /*------------|------------\-------------*/ \ /* [ non-member or static function ] */ \ ( (( defaultcall, - , 1 )) \ /* [ member function pointers ] */ \ , (( defaultcall, - , 1 )) \ (( thiscall , , 0 )) \ (( cdecl , __cdecl , 1 )) )
The only problem left, is that the preprocessing code will need a minor adjustment, so a cell can be marked as explicitly empty (the table above can't be safely handled by the preprocessor)...
Right, but ideally you would also really need to be able to detect whether __thiscall is indeed the default, or if a command line switch has overridden it.
...and (this one won't go away unless Microsoft implements __thiscall in their frontend, too) that we may have to reconfigure the library when messing with the default calling convention.
Setting the default to '__cdecl' in this particular would cause an ODR violation.
I know, this whole area of that compiler is a real pain. Thanks for the response. John.

John Maddock wrote:
I can see that argument for decomposing the arity, I'm less sure about the others, I've got no imagination I guess ;-)
Let's try a simple example:
Writing a wrapper functor like 'boost::mem_fn' on top of this library we'ld use
template<typename MemFnPtr> mem_fn_functor<MemFnPtr> mem_fn(MemFnPtr member_function_pointer);
to let the library handle all the cv, cdecl, ellipsis stuff for us. This way deduction doesn't hand us all parameters. We'll need them to build up the operator() member in the functor, though.
Right, it's how you build up the operator() that I'm stuck on, I can see how you can generate the required signature, and you can use that to *declare* the right operator, but how do you then implement the body of the function when you don't know how many parameters there are? Sorry if I'm being dense!
This is the only place which needs per-arity-code. Pseudo-code ('#' denotes preprocessing - or a lot of typing): template<std::size_t Arity> struct invokers; #FOR ARITY=0 TO MAX_ARITY template<> invokers struct< ARITY > { template<typename F> struct functor { #FOR PARAM_INDEX=0 TO ARITY-1 // <-- get parameter types for operator() #ENDFOR } } #ENDFOR However, at this point we only have to deal with the arity, not with ellipsis, CC-attributes, cv for member function pointers and ptr/ref for static function pointers (which makes things either incomplete, obscure or huge) because it's all contained in F.
One thing I've been meaning to look into, but haven't had the time yet: how do you handle __stdcall/__fastcall etc?
There is a configuration table that defines which ones to use (see below for an example).
Last time I tried to write partial specialisations that would select function types with specific calling
Because of ODR ?
No just couldn't get it to work reliably, maybe I didn't try hard enough, I was in a rush at the time...
Simple: in case custom calling conventions are configured, the default one is assumed (the first row in the table) to be among them (so there is no specialization without explicit attributation, then).
I saw the mention in the docs, but I suspect most users won't be familiar with the preprocessing library and how to set this up. An example would help a great deal (I suspect most folks will just cut and paste the example in any case).
Right. It would be better to have this inlined in the docs.
In fact if the code is robust enough (no idea if it is as I haven't had a chance to test it), I would favour enabling support for all the VC calling conventions by default for that compiler, and adding a separate preprocessed header for that one as well.
I was not sure on reasonable defaults. That's why I set the default to 100% C++, for now. Further I thought it would be "sticking to Boost policy": <CITE> Why doesn't bind automatically recognize nonstandard functions? Non-portable extensions, in general, should default to off to prevent vendor lock-in. Had the appropriate macros been defined automatically, you could have accidentally taken advantage of them without realizing that your code is, perhaps, no longer portable. </CITE> Personally, I have nothing against enabling it by default, as I like things to just work out of the box. Talking about preprocessed files: Generally, I'ld like to keep the preprocessed files compiler-neutral (dispatching at file level e.g. "partial template specialization or not" and using trigraphs or digraphs in the generator code for small in-place workarounds and preprocessing with *graph-substitution disabled, substituting them afterwards so *graph support is only needed when using the library in preprocessing mode (*1) (*2)). This way it is possible to generate the preprocessed files with a single run for all compilers without requiring them to be installed ("cross preprocessing" - so to say ;-) ). Because of this, I'ld like to generalize your idea of "adding preprocessed files for MSVC" to "adding preprocessed files for possible default configurations". Another level of indirection, which can be expressed as functions like this: part_of_filename_or_path(default_configuration(compiler,compiler_settings)) instead of: part_of_filename_or_path(compiler,compiler_settings) as several compilers may share the same default configuration but may require different workarounds. (*1) The preprocessor of GCC is available as prebuilt binary for pretty much every platform you can possibly run a compiler on and allows switching trigraph substitution on and off. Wave will be another option for this task. (*2) Currently there is a Perl-script which when preprocessed and run dumps and beautifies here-documents (with an '#include' in the un-preprocessed script) to create the preprocessed files. While this works for me and is a and funny hack (and the finalizing trigraph substitution could be done there) it is better to replace this with a C++ program using Boost.Regex and a Jamfile to control the preprocessing, in the long term.
conventions I couldn't get it to work. There's also the horrible __thiscall thing: you can't explicitly declare a member function pointer type with __thiscall, but it is usually (but not always) the default, so whether or
Even worse:
int(X::*)(int) <=> int( "__thiscall" X::*)(int) int(X::*)(...) <=> int( __cdecl X::*)(...)
This is why you can configure, whether a calling convention should support variadic functions or not.
Yep, good.
So a suitable configuration would be to have an "explicit" thiscall calling convention with an empty attributation modifier, not allowing variadic functions.
[ ATTENTION: Not a valid configuration (see below) ]
#define BOOST_FT_CALLING_CONVENTIONS \ /*---------------------------------------*/ \ /* name | modifier \ allows '...' */ \ /*------------|------------\-------------*/ \ /* [ non-member or static function ] */ \ ( (( defaultcall, - , 1 )) \ /* [ member function pointers ] */ \ , (( defaultcall, - , 1 )) \ (( thiscall , , 0 )) \ (( cdecl , __cdecl , 1 )) )
The only problem left, is that the preprocessing code will need a minor adjustment, so a cell can be marked as explicitly empty (the table above can't be safely handled by the preprocessor)...
Right, but ideally you would also really need to be able to detect whether __thiscall is indeed the default, or if a command line switch has overridden it.
Is there some macro that tells me the default calling convention ? If so, it's doable. Well, we can detect this at template level but it won't help us much, because the following code of course can't work: #if ! boost::is_same< void(), all_the_others > #define __thiscall // or whatever... #endif ;-) Except when writing a small code generator that dumps a configuration snippet for MSVC, of course.
...and (this one won't go away unless Microsoft implements __thiscall in their frontend, too) that we may have to reconfigure the library when messing with the default calling convention.
Setting the default to '__cdecl' in this particular would cause an ODR violation.
Or only use overloading (we only need a declaration) and the "typeof-bug" for MSVC<8 and hope the "no explicit __thiscall issue" will be gone in the final version 8...
I know, this whole area of that compiler is a real pain.
It says: "keyword reserved for future use", seems they just forgot... Regards, Tobias
participants (7)
-
Chris Uzdavinis
-
David Abrahams
-
Jody Hagins
-
John Maddock
-
Paul Mensonides
-
Rob Stewart
-
Tobias Schwinger