A proposal for the creation of a new module, Core Type Traits
This is a more formal statement of the same suggestion I've already given in
other threads.
Currently, library developers who need type traits functionality face a
choice: either to use the TypeTraits library, and make their libraries
dependent on MPL, Preprocessor, TypeOf and Utility, or to duplicate its
functionality locally.
Similarly, developers who want to specialize a type trait on a type of
theirs, even a trivial empty struct, need to include the TypeTraits headers
for the traits to be specialized, which brings in MPL and Preprocessor on a
header-level, and makes their module dependent on TypeTraits and its
dependencies listed above.
Local replication, as in Boost.Move, rightly attracts charges of code
duplication, with the associated potential maintenance problems when the
implementations diverge.
These two problems could be avoided if we were to have core type traits
whose interface consisted solely of a member integral constant called
"value", without any other required members or base classes, and which were
only allowed to depend on Config or StaticAssert.
This solves the second problem because it will then be possible for a
developer to specialize a trait, such as is_pod, by using a forward
declaration of the form
template< class T > struct is_pod;
in the appropriate namespace, like boost::core_type_traits, and then adding
his specializations:
template<> struct is_pod< blank >
{
BOOST_STATIC_CONSTANT( bool, value = true );
};
without including ANY additional headers.
If we look at the current implementation of Boost.TypeTraits, for example
is_pointer, we see this:
template< typename T >
struct is_pointer_impl
{
BOOST_STATIC_CONSTANT(bool, value =
(::boost::type_traits::ice_and<
::boost::detail::is_pointer_helper
These two problems could be avoided if we were to have core type traits whose interface consisted solely of a member integral constant called "value", without any other required members or base classes, and which were only allowed to depend on Config or StaticAssert.
I'm less keen on this because it fractures the lib into two conceptually different parts. Let me mess around with some code and see if something better is possible. John.
John Maddock wrote:
These two problems could be avoided if we were to have core type traits whose interface consisted solely of a member integral constant called "value", without any other required members or base classes, and which were only allowed to depend on Config or StaticAssert.
I'm less keen on this because it fractures the lib into two conceptually different parts. Let me mess around with some code and see if something better is possible.
As I tried to explain further down, it simply separates the two already existing layers in the library: the part that computes the value of the trait, which results in something like is_pointer_impl<T>::value, and the part that defines the trait, which is generally implemented like this: BOOST_TT_AUX_BOOL_TRAIT_DEF1(is_pointer,T,::boost::detail::is_pointer_impl<T>::value) This separation is natural, in that the library already does it. You're right that some traits will not be able to fit into the core part, most obviously common_type. But the users of the core part generally do not need them; and for those who do, well, they'll have to bite the bullet and use TypeTraits. Or perhaps I'm missing your point?
These two problems could be avoided if we were to have core type traits > whose interface consisted solely of a member integral constant called > "value", without any other required members or base classes, and which > were only allowed to depend on Config or StaticAssert.
I'm less keen on this because it fractures the lib into two conceptually different parts. Let me mess around with some code and see if something better is possible.
As I tried to explain further down, it simply separates the two already existing layers in the library: the part that computes the value of the trait, which results in something like is_pointer_impl<T>::value, and the part that defines the trait, which is generally implemented like this:
BOOST_TT_AUX_BOOL_TRAIT_DEF1(is_pointer,T,::boost::detail::is_pointer_impl<T>::value)
This separation is natural, in that the library already does it.
You're right that some traits will not be able to fit into the core part, most obviously common_type. But the users of the core part generally do not need them; and for those who do, well, they'll have to bite the bullet and use TypeTraits.
Or perhaps I'm missing your point?
I think so - the inner detail layer is exactly that - an implementation detail. It's also a horrible mess that got added when I wasn't looking when mpl was added to Boost - that was sort of OK then as it seemed to be the only way to make things work, but that's no longer true and there's an opportunity here to remove all that obfuscation and crud and produce something that's actually halfway readable and maintainable. But my broader point was that this generates two separate traits called is_pod (for example), with slightly different interfaces and conceptual requirements on usage. IMO this is a "really bad thing" and we can do much better. John.
John Maddock wrote:
But my broader point was that this generates two separate traits called is_pod (for example), with slightly different interfaces and conceptual requirements on usage. IMO this is a "really bad thing" and we can do much better.
Well yes, it does, and this is kind of the point, isn't it? I already explained why I want the core type traits to have a minimal interface: so that one can specialize or implement a core type trait without having to include any type traits header, core or otherwise. You define ::value and that's it, you have a bona fide trait. Others obviously don't like this interface, they want something "better". But anything better makes it worse for my purposes, so the improved version is of no use to me. So we'll be going to be stuck with two interfaces, and the best we can do is implement the richer interface in terms of the spartan one, so that specializations of the lower layer are automatically reflected in the higher one.
John Maddock wrote:
there's an opportunity here to remove all that obfuscation and crud and produce something that's actually halfway readable and maintainable.
Just on the topic of maintenance: A C++17 style voider works with GCC 3.2 and later, VS8 and later and various HP aCC, Sun Studio, Intel and other compilers listed here: https://open.cdash.org/index.php?project=CMake&date=2015-01-05 Only VS 7.1 chokes on it. Thanks, Steve.
there's an opportunity here to remove all that obfuscation and crud and produce something that's actually halfway readable and maintainable.
Just on the topic of maintenance:
A C++17 style voider works with GCC 3.2 and later, VS8 and later and various HP aCC, Sun Studio, Intel and other compilers listed here:
https://open.cdash.org/index.php?project=CMake&date=2015-01-05
Only VS 7.1 chokes on it.
What's a C++ 17 style voider? John.
John Maddock wrote:
there's an opportunity here to remove all that obfuscation and crud and produce something that's actually halfway readable and maintainable.
Just on the topic of maintenance:
A C++17 style voider works with GCC 3.2 and later, VS8 and later and various HP aCC, Sun Studio, Intel and other compilers listed here:
https://open.cdash.org/index.php?project=CMake&date=2015-01-05
Only VS 7.1 chokes on it.
What's a C++ 17 style voider?
See
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf
https://www.youtube.com/watch?feature=player_detailpage&v=a0FliKwcwXE#t=1740
Even if using VS8, GCC3.2 etc you can still do this:
template<typename>
struct voider { typedef void type; };
struct true_type
{
enum { value = true };
};
struct false_type
{
enum { value = false };
};
template
Le 07/01/15 21:20, Peter Dimov a écrit :
This is a more formal statement of the same suggestion I've already given in other threads.
Currently, library developers who need type traits functionality face a choice: either to use the TypeTraits library, and make their libraries dependent on MPL, Preprocessor, TypeOf and Utility, or to duplicate its functionality locally.
Similarly, developers who want to specialize a type trait on a type of theirs, even a trivial empty struct, need to include the TypeTraits headers for the traits to be specialized, which brings in MPL and Preprocessor on a header-level, and makes their module dependent on TypeTraits and its dependencies listed above.
Local replication, as in Boost.Move, rightly attracts charges of code duplication, with the associated potential maintenance problems when the implementations diverge.
These two problems could be avoided if we were to have core type traits whose interface consisted solely of a member integral constant called "value", without any other required members or base classes, and which were only allowed to depend on Config or StaticAssert.
This solves the second problem because it will then be possible for a developer to specialize a trait, such as is_pod, by using a forward declaration of the form
template< class T > struct is_pod;
in the appropriate namespace, like boost::core_type_traits, and then adding his specializations:
template<> struct is_pod< blank > { BOOST_STATIC_CONSTANT( bool, value = true ); };
without including ANY additional headers.
If we look at the current implementation of Boost.TypeTraits, for example is_pointer, we see this:
template< typename T > struct is_pointer_impl { BOOST_STATIC_CONSTANT(bool, value = (::boost::type_traits::ice_and< ::boost::detail::is_pointer_helper
::value , ::boost::type_traits::ice_not< ::boost::is_member_pointer<T>::value >::value >::value) ); }; in namespace detail, and then this:
BOOST_TT_AUX_BOOL_TRAIT_DEF1(is_pointer,T,::boost::detail::is_pointer_impl<T>::value)
which takes care of everything else.
Note that is_pointer_impl is EXACTLY what we need! It is the core implementation of the trait and exposes only ::value and nothing else. The full-featured boost::is_pointer is built on top of that.
If detail::is_pointer_impl was called core_type_traits::is_pointer, this would immediately take care of the two problems at the top of this post, while not affecting the users of Boost.TypeTraits in any way. I like this proposal. However, I'm wondering if BOOST_TT_AUX_BOOL_TRAIT_DEF1 is so heavy so that it can not be on CoreTypeTraitts?
But how this split would get ride of the MPL dependency? I suspect that you want common_type to stay in Boost.TypeTraits so that the Boost.CoreTypeTraits doesn't depend on Boost.Typeof. Couldn't Boost.TypeOf go also to CoreTypeTraits?
In particular, note that if a developer specializes detail::is_pointer_impl, is_pointer automatically picks up this specialization.
You surely mean core_type_traits::is_pointer.
Therefore,
I propose that a new module, libs/core_type_traits, is created, which:
- provides core type traits that only expose a member integral constant called "value" as part of its documented interface and nothing else;
- allows these traits to be forward declared, without requiring header inclusion;
- is not allowed to depend on anything except Config or StaticAssert; Could CoreTypeTraits depend on Core? Boost.Preprocessor?
- is the place to which TypeTraits's is_pointer_impls gradually migrate, along with their tests.
This
- solves the dependency problems some of us are facing,
- avoids unnecessary code duplication,
- still allows type trait specialization, with clients of both libraries automatically picking up the specialization,
- is backward compatible with respect to existing uses of Boost.TypeTraits because the new library will contain the same code and the same tests.
Thank you for your attention. :-)
Thanks for resurecting this issue, Vicente
participants (4)
-
John Maddock
-
Peter Dimov
-
Stephen Kelly
-
Vicente J. Botet Escriba