
Paul Mensonides wrote:
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Tobias Schwinger
That will remove any cv-qualifiers on a pointer to function
(as in a
"const pointer to function"), but it won't remove the cv-qualifiers from a raw function type. There is no way to add or remove such qualifiers without taking the type apart and putting it
back together with or without the cv-qualification.
Is either way defined behaviour? Got some standard references for me, perhaps?
----- 8.3.5/4
A cv-qualifier-seq shall only be part of the function type for a nonstatic member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type, i.e., it does not create a cv-qualified function type. In fact, if at any time in the determination of a type a cv-qualified function type is formed, the program is ill-formed. -----
It is not too clear whether it applies to partial template specialization, since we don't really "form" a type - we just consider a possibitity: typedef int my_const_function() const; template<typename T> struct remove_const { typedef T type; }; template<typename T> struct remove_const< T const > { typedef T type; }; // somwhere else remove_const<my_const_function>::type // ill-formed?
There might be more references, but this gives a significantly strong implication that cv-qualification on function types is radically different than normal cv-qualification.
...and that cv-qualification on function types is intended for nonstatic member functions only (at least that's the impression I get from reading that paragraph).
The cv-qualifiers are modifying the implicit 'this' parameter. I.e. the cv-qualifiers don't modify the function type (all functions are 'const' because C++ doesn't treat functions as first-class objects); they modify an implicit formal parameter. So attempting to remove cv-qualification this way would be akin to transforming 'void (const int*)' into 'void (int*)'.
I don't find it very convincing -- we're talking about two different pairs of shoes here (syntax and type system vs. semantic treatment), IMO.
struct C { };
template<class T> struct is_const_function : is_pointer_to_const_member_function<T C::*> { };
Another thought on this one: as you said already, before forming a type C::*T we have to make sure it works for the T we have. But can we do it with T actually being a type which renders our program ill-formed when encountered (and without relying on undefined behaviour)? <snip>
In this particular case it doesn't cost much. But if I'm going to do a trivial thing as finding out whether 'int' is a (any cv-qualified) function I need a second non-trivial template instantiation and a couple of trivial ones as you mentioned already. I'm not sure this feature is worth its price. Further, if your compiler is smart enough you can easily write it yourself if you need it.
Yes. On the same token, however, I can just as easily write any other sort of function type manipulation that I might need. The point is that a library like yours should prevent the need for me to do it.
Right. But is there a need for raw, cv-qualified functions in the first place?
If the feature is not used, the only possible *significant* compile-time overhead that I see is preprocessing.
Right. However, I'm not concerned about preprocessing at all (I use preprocessed files). I also don't care about parsing time (it's comparatively little and it costs once per translation unit). What counts is the complexitiy of using the metafunctions.
This can be solved by making headers more granular (not a commentary on your library) and simply not including it. If it is used, then the user pays for it with a few template instantiations--just like anything else. From an implementation point-of-view (meaning the cost on your time personally), supporting this would be fairly easy if you don't try to make it work on compilers that are fundamentally broken in this area.
I don't find your suggestion of testing C::*T instead of T generally unattractive (and ideally it might simplify things). The conditions for applying it would be: (when integrating) - be sure your suggestion builts on solid C++ (see above) - find a nice name to disambiguate const and volatile property tags so I won't need an underscore suffix (when the library is used) - it works with the compiler - the configuration allows it by specifying the same calling conventions for member and non member functions (and there is no MSVC __thiscall weirdness around)
If that is the case, then you shouldn't be supporting open
variadics.
Support for that adds significant overhead to the implementation. Granted, variadic function types are more common than cv-qualified function types, but they are still quite rare.
Can't really say 'printf' is a dark corner of the language...
The type of 'printf' may have C linkage which you can't do anything with on any conforming compiler. Basically, 'printf' is untouchable (in this context) in portable code.
Oh - I didn't mean 'printf' personally! It's just that the very first library function a C programmer usually learns has a variadic argument list, so I found it wrong to refer to open variadics as being an esoteric feature.
"Dark history", perhaps. And open variadics can be quite useful. We shouldn't make assumptions on how many people use them -- variadic functions have been there for a long time.
Trust me, I'm a big fan of the concept of variadics. Given "reasonable" language support, they are a very enabling feature for a variety of things. (This is one of the reasons why I'm glad that C is still around. IMO, C++ tends to overlook the fundamental building blocks (e.g. procedural programming) as it grows. In some sense, C keeps C++ grounded. Of course, C is not the be all and end all of procedural programming. By now, we should have nested functions, with them comes closures, and with closures comes anonymous functions (i.e. lambda functions). These are fundamental building blocks.) Unfortunately, we don't yet have reasonable variadic support in C++. We need typesafe variadic functions, variadic templates, and variadic macros.
I agree.
However right now, given that argument lists are an iterative syntactic structure (currently at odds with generic programming), that current variadic functions aren't typesafe (at odds with generic programming), and that you can't portably pass anything to them except POD's (at odds with generic programming)--it's a safe bet that function type manipulation (which would likely be done in generic code) of variadic function types will be extremely rare.
I have to annotate a tiny bit of rationale here: int func(int...) can be invoked like func(1), so it should be a valid argument for a generic facility that accepts a unary callable argument which in turn has an int parameter.
Variadic functions (specifically, how to use them even to the point of how to invoke them) are way too semantically dependent on extra-linguistic knowledge like documentation. I certainly don't think you should remove support for them--I just see its utility as on par with cv-qualified function types--yet supporting it effectively doubles all of the template specializations required.
From what I have benchmarked so far it seems that adding (partial) specializations has a noticably smaller impact on the compilation time than adding instantiations (as required for decision making and matching these specializations twice).
I don't buy the 'cost' argument as opposed to the 'completeness' argument, but I'm not terribly concerned over the lack of support. However, I'm a would-be user of a library, not the library author. If I was the library author, completeness would be a worthwhile goal in and of itself--it is a requirement for mastery of the subject. As a library author in other areas, the most annoying limitations are often those corner cases that you can do nothing about (and this isn't one of them), but present a barrior to completeness. It's the difference between "as good as can be given x, y, and z" and "ideal". My disagreement is more of a disagreement with perspective rather than over a particular feature.
Here is no disagreement at all (at least at this abstract level): I'm all for completeness but I'm not convinced that what you are proposing is in fact about completeness and not about emulating consistency where there is none. Plus I'm wondering whether it's implementable in ISO C++ at all. Thanks, Tobias