Hi everyone,
are there any guidelines for boost libraries on when to use traits
classes and when to use a metafunction for each member of a would-be
traits class?
I've seen both in boost libraries. c++11 uses traits classes
(iterator_traits, pointer traits, ...), but Abrahams and Gurtovoy [1]
argue that traits classes ("traits blobs") should be avoided "at all
costs" because they are an unnecessary concatenation of multiple
metafunctions into one metafunction with multiple return values.
in practice, this means that if a single 'traits class member' aka
'metafunction result' ought to differ from the defaults, the entire
traits class has to be reimplemented by the user, not only the
metafunction whose result ought to differ from the default.
on the other hand, asking a user to implement 5 or more metafunctions is
much more tedious than implementing a traits class.
possibility 1)
template<...> struct property1 : mpl::bool_<...>{};
template<...> struct property2 : mpl::bool_<...>{};
template<...> struct property3 : mpl::bool_<...>{};
2)
template<...>
struct ..._traits{
typedef mpl::bool_<...> property1;
typedef mpl::bool_<...> property2;
typedef mpl::bool_<...> property3;
};
you could provide both, but in which "direction"?
does a traits class refer to metafunctions, or do metafunctions refer to
a traits class?
a)
template<...>
struct ..._traits{
typedef typename property1<...>::type property1;
typedef typename property2<...>::type property2;
typedef typename property3<...>::type property3;
};
b)
template<...> struct property1 : ..._traits<...>::property1{}
template<...> struct property2 : ..._traits<...>::property2{}
template<...> struct property3 : ..._traits<...>::property3{}
which is accessed when "calling" the metafunction?
it seems to me that metafunctions are better for the "caller" and traits
classes are better for the implementor of the traits/metafunctions.
in my case, the caller is a library and the implementor is the user, so
you might choose a traits class over metafunctions. but what if a new
member is added that wasn't there before (like "property4").
the implementor of an (old) traits class could not have known about the
new member, so accessing the new member (by the library) will result in
an error, while a new metafunction "property4" could simply return a
default.
should new properties of traits classes only be accessed after making
sure they exist, using SFINAE?
there isn't a precedent for this in c++11. only new traits classes were
introduced, but no new members of already existing traits classes.
should metafunctions always be preferred? why did c++11 choose traits
classes over metafunctions, even though the concept of a metafunction
was introduced in c++11 (
On Wednesday 06 March 2013 20:36:20 Stefan Strasser wrote:
Hi everyone,
are there any guidelines for boost libraries on when to use traits classes and when to use a metafunction for each member of a would-be traits class?
I'm not aware of any guidelines but I typically use traits when the result types are related and may be used together by the user. Consider iterator_traits, which provides several properties of an iterator. Algorithm and iterator adapter implementors will likely use value_type, reference and category typedefs of iterator_traits. If it was divided into several distinct metafunctions one would have to instantiate several templates which would have a compile time impact. OTOH, when metafunctions are rarely used together or unrelated it is better to keep them separated.
Am 06.03.2013 20:50, schrieb Andrey Semashev:
On Wednesday 06 March 2013 20:36:20 Stefan Strasser wrote:
Hi everyone,
are there any guidelines for boost libraries on when to use traits classes and when to use a metafunction for each member of a would-be traits class?
I'm not aware of any guidelines but I typically use traits when the result types are related and may be used together by the user.
what do you do when there is both, properties that are related and ones that are not?
Consider iterator_traits, which provides several properties of an iterator.
consider c++11 allocator_traits and propagate_* http://en.cppreference.com/w/cpp/memory/allocator_traits
On Wed, Mar 6, 2013 at 11:36 AM, Stefan Strasser
Hi everyone,
are there any guidelines for boost libraries on when to use traits classes and when to use a metafunction for each member of a would-be traits class?
I'd say use your best judgment. I've seen both in boost libraries. c++11 uses traits classes
(iterator_traits, pointer traits, ...), but Abrahams and Gurtovoy [1] argue that traits classes ("traits blobs") should be avoided "at all costs" because they are an unnecessary concatenation of multiple metafunctions into one metafunction with multiple return values.
in practice, this means that if a single 'traits class member' aka 'metafunction result' ought to differ from the defaults, the entire traits class has to be reimplemented by the user, not only the metafunction whose result ought to differ from the default.
One way to mitigate that is to provide a standard, default implementation of the traits class from which specializations can inherit and override only that which is needed. on the other hand, asking a user to implement 5 or more metafunctions is
much more tedious than implementing a traits class.
Agreed. If this is a likely scenario, I'd think an interface that allows you to specialize a traits class should at least be considered. [...] - Jeff
on Wed Mar 06 2013, Stefan Strasser
Hi everyone,
are there any guidelines for boost libraries on when to use traits classes and when to use a metafunction for each member of a would-be traits class?
Yeah, use metafunctions :-)
I've seen both in boost libraries. c++11 uses traits classes (iterator_traits, pointer traits, ...), but Abrahams and Gurtovoy [1] argue that traits classes ("traits blobs") should be avoided "at all costs" because they are an unnecessary concatenation of multiple metafunctions into one metafunction with multiple return values.
in practice, this means that if a single 'traits class member' aka metafunction result' ought to differ from the defaults, the entire traits class has to be reimplemented by the user, not only the metafunction whose result ought to differ from the default. on the other hand, asking a user to implement 5 or more metafunctions is much more tedious than implementing a traits class.
Not having the metafunctions decreases interoperability. Having the metafunctions and the traits classes increases confusion and clutter. It's already pretty darned tedious to produce specializations of another library's templates, because of the namespaces to open/close. I don't think asking users to provide a traits class offers much convenience advantage at all. There's usually a better way to get convenience, and that is to have the metafunctions' default implementations reach into one of their parameters for their results.
possibility 1)
template<...> struct property1 : mpl::bool_<...>{}; template<...> struct property2 : mpl::bool_<...>{}; template<...> struct property3 : mpl::bool_<...>{};
2)
template<...> struct ..._traits{ typedef mpl::bool_<...> property1; typedef mpl::bool_<...> property2; typedef mpl::bool_<...> property3; };
you could provide both, but in which "direction"? does a traits class refer to metafunctions, or do metafunctions refer to a traits class?
a)
template<...> struct ..._traits{ typedef typename property1<...>::type property1; typedef typename property2<...>::type property2; typedef typename property3<...>::type property3; };
b)
template<...> struct property1 : ..._traits<...>::property1{} template<...> struct property2 : ..._traits<...>::property2{} template<...> struct property3 : ..._traits<...>::property3{}
which is accessed when "calling" the metafunction?
If you think specializing metafunctions is painful, then clearly you need (b). But I don't think that.
it seems to me that metafunctions are better for the "caller" and traits classes are better for the implementor of the traits/metafunctions.
Only if the implementor has would have to implement all or most of the metafunctions, and then the convenience is only marginal, IMO.
in my case, the caller is a library and the implementor is the user, so you might choose a traits class over metafunctions. but what if a new member is added that wasn't there before (like "property4"). the implementor of an (old) traits class could not have known about the new member, so accessing the new member (by the library) will result in an error, while a new metafunction "property4" could simply return a default. should new properties of traits classes only be accessed after making sure they exist, using SFINAE? there isn't a precedent for this in c++11. only new traits classes were introduced, but no new members of already existing traits classes.
should metafunctions always be preferred?
IMO unless there's a compelling argument against them, yes.
why did c++11 choose traits classes over metafunctions, even though the concept of a metafunction was introduced in c++11 (
).
Because I got lazy and didn't try to educate all the contributors to the standard and control the design of the standard library in that way. P.S. Have you seen what they did with enable_if? -- Dave Abrahams
why did c++11 choose traits classes over metafunctions, even though the concept of a metafunction was introduced in c++11 (
). ...
P.S. Have you seen what they did with enable_if?
Do you mean that the standard version takes the condition as a boolean parameter rather than a type parameter? If so, that actually seems more natural to me. Regards, Nate
31.03.2013 10:42, Nathan Ridge wrote:
P.S. Have you seen what they did with enable_if?
Do you mean that the standard version takes the condition as a boolean parameter rather than a type parameter? If so, that actually seems more natural to me.
I think bigger issue is that they didn't use template alias, which would remove need for "typename [...]::type". http://www.stroustrup.com/C++11FAQ.html#template-alias -- Evgeny Panasyuk
on Sat Mar 30 2013, Nathan Ridge
why did c++11 choose traits classes over metafunctions, even though the concept of a metafunction was introduced in c++11 (
). ...
P.S. Have you seen what they did with enable_if?
Do you mean that the standard version takes the condition as a boolean parameter rather than a type parameter? If so, that actually seems more natural to me.
More natural in some subjective sense but but less interoperable. Oh well. -- Dave Abrahams
Am 31.03.2013 16:36, schrieb Dave Abrahams:
on Sat Mar 30 2013, Nathan Ridge
wrote: why did c++11 choose traits classes over metafunctions, even though the concept of a metafunction was introduced in c++11 (
). ...
P.S. Have you seen what they did with enable_if?
Do you mean that the standard version takes the condition as a boolean parameter rather than a type parameter? If so, that actually seems more natural to me.
More natural in some subjective sense but but less interoperable. Oh well.
I don't understand why they didn't get rid of the "metafunction" concept as we use it today entirely, but introduce it into the official standard via type traits. E.g. via a "constexpr" that can return types, or some other native syntax for metafunctions.
On 31 March 2013 18:31, Stefan Strasser wrote:
I don't understand why they didn't get rid of the "metafunction" concept as we use it today entirely, but introduce it into the official standard via type traits. E.g. via a "constexpr" that can return types, or some other native syntax for metafunctions.
And I don't understand why people expect the standard to emerge
perfectly formed from some magic source of an ideal language.
This stuff doesn't just happen by magic, someone has to propose it and
the people involved have limited time and resources.
There are good reasons for the committee to standardise existing
practice instead of trying to invent entirely new tools that noone has
any experience with. The
Am 31.03.2013 19:46, schrieb Jonathan Wakely:
On 31 March 2013 18:31, Stefan Strasser wrote:
I don't understand why they didn't get rid of the "metafunction" concept as we use it today entirely, but introduce it into the official standard via type traits. E.g. via a "constexpr" that can return types, or some other native syntax for metafunctions.
And I don't understand why people expect the standard to emerge perfectly formed from some magic source of an ideal language.
If you think there should be something else then get involved and propose it.
I can understand your viewpoint, but even if I wasn't involved in IT at the time, and certainly not at that level, I can still say that it always seemed rather obvious to me that the "metafunction concept" of Boost.MPL is a workaround because of missing native language features (using template, class, typedef). so I was surprised that it was introduced into the standard when there was a chance to natively support metafunctions. that isn't saying that anyone did a bad job when introducing type traits, and I'm sorry if it was perceived that way.
on Sun Mar 31 2013, Stefan Strasser
I can understand your viewpoint, but even if I wasn't involved in IT at the time, and certainly not at that level, I can still say that it always seemed rather obvious to me that the "metafunction concept" of Boost.MPL is a workaround because of missing native language features (using template, class, typedef). so I was surprised that it was introduced into the standard when there was a chance to natively support metafunctions.
There's a chance to support lots of things natively that nobody has yet implemented, used, formalized, or proposed. Invent the mechanism and put it in a real compiler, and *then* maybe, if it gets used, it will be time to propose it for standardization.
that isn't saying that anyone did a bad job when introducing type traits, and I'm sorry if it was perceived that way.
I think you're missing the point. The committee tries hard not to engage in feature invention, and in the rare cases when it does, somebody creates an actual implementation of the feature. -- Dave Abrahams
Am 01.04.2013 00:49, schrieb Dave Abrahams:
There's a chance to support lots of things natively that nobody has yet implemented, used, formalized, or proposed. Invent the mechanism and put it in a real compiler, and *then* maybe, if it gets used, it will be time to propose it for standardization.
I think you're missing the point. The committee tries hard not to engage in feature invention, and in the rare cases when it does, somebody creates an actual implementation of the feature.
then I don't think it'll ever happen. there is very little reason for compiler implementors to improve the syntax of an existing feature, as it will almost never be used unless it's standardized. the situation with metafunctions is more like c++11 lambdas. (I'm guessing here) committee members looked at other languages and library implementations of lambdas like Boost.Phoenix and saw the need for language support. Was there a production compiler that implemented language support for lambdas before it was standardized?
On 1 April 2013 19:35, Stefan Strasser wrote:
Am 01.04.2013 00:49, schrieb Dave Abrahams:
There's a chance to support lots of things natively that nobody has yet implemented, used, formalized, or proposed. Invent the mechanism and put it in a real compiler, and *then* maybe, if it gets used, it will be time to propose it for standardization.
I think you're missing the point. The committee tries hard not to engage in feature invention, and in the rare cases when it does, somebody creates an actual implementation of the feature.
then I don't think it'll ever happen. there is very little reason for compiler implementors to improve the syntax of an existing feature, as it will almost never be used unless it's standardized. the situation with metafunctions is more like c++11 lambdas. (I'm guessing here) committee members looked at other languages and library implementations of lambdas like Boost.Phoenix and saw the need for language support. Was there a production compiler that implemented language support for lambdas before it was standardized?
Yes. GCC 4.6 supports lambdas and was released 6 months before C++11 was published (on the day the FDIS was published, in fact.) For a better example, Apple added support for their Blocks extension to both GCC and Clang without it being in any standard. Both GCC and Clang supported variadic templates long before C++11. Extern templates and allowing types without linkage to be template arguments were supported by several production compilers for many years before being standardised.
on Sun Mar 31 2013, Jonathan Wakely
On 31 March 2013 18:31, Stefan Strasser wrote:
I don't understand why they didn't get rid of the "metafunction" concept as we use it today entirely, but introduce it into the official standard via type traits. E.g. via a "constexpr" that can return types, or some other native syntax for metafunctions.
And I don't understand why people expect the standard to emerge perfectly formed from some magic source of an ideal language.
This stuff doesn't just happen by magic, someone has to propose it and the people involved have limited time and resources.
There are good reasons for the committee to standardise existing practice instead of trying to invent entirely new tools that noone has any experience with. The
header we have today is largely based on ideas that got proposed because they were known to work and were useful in the real world. When was added to TR1 constexpr didn't exist. When constexpr was added there were no working implementations to experiment with.
And the specification was—and probably still is—correspondingly broken. That's what usually happens when you standardize something that isn't implemented. -- Dave Abrahams
participants (7)
-
Andrey Semashev
-
Dave Abrahams
-
Evgeny Panasyuk
-
Jeffrey Lee Hellrung, Jr.
-
Jonathan Wakely
-
Nathan Ridge
-
Stefan Strasser