Pervasive dependency upon mpl::bool_
We have lots of headers that depend on mpl::bool_ in order to define traits classes as deriving from mpl::true_ or mpl::false_. I don't think that this dependency is necessary even for MPL interoperability. The normal way of template<class T> struct trait { BOOST_STATIC_CONSTANT( bool, value = false ); }; should work just as well. I think. As one example, there's detail/blank.hpp, which starts with #include "boost/mpl/bool.hpp" #include "boost/type_traits/is_empty.hpp" #include "boost/type_traits/is_pod.hpp" #include "boost/type_traits/is_stateless.hpp" It doesn't really need these includes. namespace boost { struct blank { }; // type traits specializations // template <> struct is_pod< blank > : mpl::true_ { }; This can be written as template<class T> struct is_pod; template<> struct is_pod< blank > { BOOST_STATIC_CONSTANT( bool, value = true ); }; which requires no includes. The problem is, though, that all type traits do derive from mpl::true_ or mpl::false_, and I'm not sure if rewriting blank.hpp in the above manner is actually correct. Should type trait specializations always derive from mpl::true_ or mpl::false_? Will I break something if I specialize a type trait to not derive from mpl::bool_? Or is a nested ::value enough? Who knows. :-) But really, if a header, whose entire purpose is to define an empty struct, can't get by without including type traits and mpl, we've lost the dependency game before it's even started.
On 1 June 2014 21:40, Peter Dimov wrote:
The problem is, though, that all type traits do derive from mpl::true_ or mpl::false_, and I'm not sure if rewriting blank.hpp in the above manner is actually correct. Should type trait specializations always derive from mpl::true_ or mpl::false_? Will I break something if I specialize a type trait to not derive from mpl::bool_? Or is a nested ::value enough? Who knows. :-)
Anecdata: I often do tag dispatching on traits and overload a function to take either std::true_type or std::false_type, where I know the trait is guaranteed to derive from one or the other (which is true for all the standard type traits, and others that I write). It wouldn't surprise me if other people do that with mpl::true_ and mpl::false_ , relying on traits deriving from exactly those types.
But really, if a header, whose entire purpose is to define an empty struct, can't get by without including type traits and mpl, we've lost the dependency game before it's even started.
That pervasive dependency and the hundreds of lines that get included are why I immediately replaced all boost type traits with the C++11 equivalents when a couple of projects moved to a C++11 compiler. Would it be possible to define true_ and false_ with a lot less code?
Jonathan Wakely wrote:
Anecdata: I often do tag dispatching on traits and overload a function to take either std::true_type or std::false_type, where I know the trait is guaranteed to derive from one or the other (which is true for all the standard type traits, and others that I write).
That's indeed a guarantee the standard gives, and since it's a "shall" in
the blanket requirements, user specializations also have to obey (were they
allowed).
I don't like it very much, though. It's just to save some typing - you could
write integral_constant
Would it be possible to define true_ and false_ with a lot less code?
Possibly. The problem is that mpl::bool_ is owned by MPL. It can impose all sorts of (potentially changing, although this is not an issue at the moment) requirements on them. None of those are actually needed by people doing tag dispatching, but we can't just kidnap mpl::true_ and mpl::false_ and decree that they shall henceforth live in Core.
On Monday 02 June 2014 00:30:12 Peter Dimov wrote:
Would it be possible to define true_ and false_ with a lot less code?
Possibly. The problem is that mpl::bool_ is owned by MPL. It can impose all sorts of (potentially changing, although this is not an issue at the moment) requirements on them. None of those are actually needed by people doing tag dispatching, but we can't just kidnap mpl::true_ and mpl::false_ and decree that they shall henceforth live in Core.
What are those requirements? Are they actually changing that often? I bet if we drop support for ancient compilers, then mpl::bool_, mpl::if_ and friends will become quite trivial and can be placed in Core just fine.
On June 1, 2014 5:30:12 PM EDT, Peter Dimov
Jonathan Wakely wrote:
Would it be possible to define true_ and false_ with a lot less code?
Possibly. The problem is that mpl::bool_ is owned by MPL. It can impose all sorts of (potentially changing, although this is not an issue at the moment) requirements on them. None of those are actually needed by people doing tag dispatching, but we can't just kidnap mpl::true_ and mpl::false_ and decree that they shall henceforth live in Core.
Put a few pieces into core and change MPL to use or derive from them. For example, mpl:: true_type can derive from a new boost::true_type (which could be an alias for std:: true_type). New and refactored code can use the latter, while legacy code can continue to use the former. ___ Rob (Sent from my portable computation engine)
On Sunday 01 June 2014 22:00:28 Jonathan Wakely wrote:
On 1 June 2014 21:40, Peter Dimov wrote:
The problem is, though, that all type traits do derive from mpl::true_ or mpl::false_, and I'm not sure if rewriting blank.hpp in the above manner is actually correct. Should type trait specializations always derive from mpl::true_ or mpl::false_? Will I break something if I specialize a type trait to not derive from mpl::bool_? Or is a nested ::value enough? Who knows. :-)
Anecdata: I often do tag dispatching on traits and overload a function to take either std::true_type or std::false_type, where I know the trait is guaranteed to derive from one or the other (which is true for all the standard type traits, and others that I write). It wouldn't surprise me if other people do that with mpl::true_ and mpl::false_ , relying on traits deriving from exactly those types.
I sometimes do this, including outside Boost.
Le 01/06/14 22:40, Peter Dimov a écrit :
We have lots of headers that depend on mpl::bool_ in order to define traits classes as deriving from mpl::true_ or mpl::false_.
I don't think that this dependency is necessary even for MPL interoperability. The normal way of
template<class T> struct trait { BOOST_STATIC_CONSTANT( bool, value = false ); };
should work just as well. I think.
As one example, there's detail/blank.hpp, which starts with
#include "boost/mpl/bool.hpp" #include "boost/type_traits/is_empty.hpp" #include "boost/type_traits/is_pod.hpp" #include "boost/type_traits/is_stateless.hpp"
It doesn't really need these includes.
namespace boost {
struct blank { };
// type traits specializations //
template <> struct is_pod< blank > : mpl::true_ { };
This can be written as
template<class T> struct is_pod;
template<> struct is_pod< blank > { BOOST_STATIC_CONSTANT( bool, value = true ); };
This will need boost/config.hpp
which requires no includes.
The problem is, though, that all type traits do derive from mpl::true_ or mpl::false_, and I'm not sure if rewriting blank.hpp in the above manner is actually correct. Should type trait specializations always derive from mpl::true_ or mpl::false_? Will I break something if I specialize a type trait to not derive from mpl::bool_? Or is a nested ::value enough? Who knows. :-) Currently the type traits derive from true_type or false_type.
is_pod
http://www.boost.org/doc/libs/1_55_0/libs/type_traits/doc/html/boost_typetra...
template <class T>
struct is_pod : public /|true_type http://www.boost.org/doc/libs/1_55_0/libs/type_traits/doc/html/boost_typetra...-or-false_type http://www.boost.org/doc/libs/1_55_0/libs/type_traits/doc/html/boost_typetra...|/ {};
The problem is that integral_constant inherits from mpl::integral_c.
I suggest to change the contents of integral_constant.hpp to something like
template
But really, if a header, whose entire purpose is to define an empty struct, can't get by without including type traits and mpl, we've lost the dependency game before it's even started.
You are right, some refactoring is needed. I propose to move this integral_constant.hpp file to Boost.Core. Best, Vicente
Vicente J. Botet Escriba wrote:
template <> struct is_pod< blank > : true_type {};
This still breaks dispatch on mpl::true_/mpl::false_. The use case is: void f( mpl::true_ ); void f( mpl::false_ ); struct X; int main() { f( boost::is_something<X>() ); } This is existing code, both inside and outside Boost. Rob Stewart wrote:
Put a few pieces into core and change MPL to use or derive from them. For example, mpl:: true_type can derive from a new boost::true_type (which could be an alias for std:: true_type).
This still breaks the above use case, as boost::is_something will derive
from boost::true_type, but not from mpl::true_.
We could add converting constructors to mpl::bool_ taking core::bool_, which
will fix the above, but may break other, more convoluted uses.
The 'proper' way to handle that (and the problem with type traits using MPL
is not limited to mpl::bool_) is to extract the actual type traits into core
type traits, which do not derive from anything, do not include MPL lambda
support, and whose implementation does not include a non-core library (which
is an issue only for type_with_alignment and common_type). Then, the current
type traits would be rewritten as
template<class T> struct is_something: integral_constant
Le 02/06/14 17:39, Peter Dimov a écrit :
Vicente J. Botet Escriba wrote:
template <> struct is_pod< blank > : true_type {};
This still breaks dispatch on mpl::true_/mpl::false_. The use case is:
void f( mpl::true_ ); void f( mpl::false_ );
struct X;
int main() { f( boost::is_something<X>() ); }
This is not documented, so the user or the boost library is using an undocumented feature and must be changed.
This is existing code, both inside and outside Boost. I hope that we don't mind breaking code that is using undocumented features.
Rob Stewart wrote:
Put a few pieces into core and change MPL to use or derive from them. For example, mpl:: true_type can derive from a new boost::true_type (which could be an alias for std:: true_type).
+1 for using the existing std when available.
This still breaks the above use case, as boost::is_something will derive from boost::true_type, but not from mpl::true_.
No from my perspective.
We could add converting constructors to mpl::bool_ taking core::bool_, which will fix the above, but may break other, more convoluted uses. Hugh
The 'proper' way to handle that (and the problem with type traits using MPL is not limited to mpl::bool_) is to extract the actual type traits into core type traits, which do not derive from anything, do not include MPL lambda support, and whose implementation does not include a non-core library (which is an issue only for type_with_alignment and common_type). Then, the current type traits would be rewritten as
template<class T> struct is_something: integral_constant
{}; and can still include the MPL scaffolding for people who like that sort of thing.
blank.hpp will then specialize the core type trait and the ordinary type trait will still pick up the specialization.
IIUC you are proposing to split the type_traits into two levels, those that don't depend on MPL and those that depend on MPL. I would like to break the dependency from type_traits and MPL if possible. And you proposal seems a good transition, but a little bit artificial. Putting MPL and Type Traits on the same module would reduce the dependencies also.
That's a real project though. Not just moving a few headers around. :-) Agreed.
But it's tractable. The good thing is that the core type traits do not have to be written all at once. We can migrate them one by one, at a leisurely pace.
Yes the best it to see case by case. Could you show a concerte/real example so that we are fixed on the proposed approach. Vicente
Vicente J. Botet Escriba wrote:
Putting MPL and Type Traits on the same module would reduce the dependencies also.
No, not really. The problem today is that when you include type traits you start depending on type traits, its dependencies, MPL, and _its_ dependencies, in turn. If we put type traits and MPL into the same module, when you include a type traits header, you will depend on the exact same set. The module count would be decreased by one, but that's not the issue - the actual number of files will stay the same.
Hello,
On Mon, Jun 2, 2014 at 12:39 PM, Peter Dimov
Vicente J. Botet Escriba wrote:
template <> struct is_pod< blank > : true_type {};
This still breaks dispatch on mpl::true_/mpl::false_. The use case is:
void f( mpl::true_ ); void f( mpl::false_ );
struct X;
int main() { f( boost::is_something<X>() ); }
This is existing code, both inside and outside Boost.
I use this a lot. But it is really easy to fix if it breaks. But, can't Boost.Core include a integral_constant and boost.mpl just use that for false_type and true_type too? Regards, -- Felipe Magno de Almeida
Vicente J. Botet Escriba wrote:
template <> struct is_pod< blank > : true_type {};
This still breaks dispatch on mpl::true_/mpl::false_. The use case is:
void f( mpl::true_ ); void f( mpl::false_ );
struct X;
int main() { f( boost::is_something<X>() ); }
This is existing code, both inside and outside Boost.
I use this a lot. But it is really easy to fix if it breaks. But, can't Boost.Core include a integral_constant and boost.mpl just use that for false_type and true_type too?
Right, what's wrong with: namespace boost{ namespace mpl{ template bool<B> struct bool_; } namespace mpl{ using boost::core::bool_; }} The idea of artificially splitting up type_traits seems pretty abhorrent to be honest. John.
On Tue, Jun 3, 2014 at 11:49 AM, John Maddock
Vicente J. Botet Escriba wrote:
Right, what's wrong with:
namespace boost{ namespace mpl{
That should be namespace core.
template bool<B> struct bool_;
}
namespace mpl{
using boost::core::bool_;
}}
Yes, I had similar idea. I'll try to extract minimal bits of MPL in a separate branch in Core, maybe dropping some workarounds in the process to reduce the amount of code.
The idea of artificially splitting up type_traits seems pretty abhorrent to be honest.
I think there's no need to split anything for cases like in blank.hpp. We can get away with a simple forward declaration: template< typename > struct is_pod; template< > struct is_pod< blank > : mpl::true_ {};
Andrey Semashev wrote:
Yes, I had similar idea. I'll try to extract minimal bits of MPL in a separate branch in Core, maybe dropping some workarounds in the process to reduce the amount of code.
Extracting the commonly used parts of MPL such as mpl::bool_, mpl::if_,
mpl::and_ will help, although I'd argue that they need to go into module
mpl_core, not into module core.
But this will not solve the problem that using boost::is_pod adds MPL to the
dependency graph. It will only let us specialize it without depending on
MPL.
On Tue, Jun 3, 2014 at 11:49 AM, John Maddock
Right, what's wrong with: [...]
This is mpl::bool_:
// bool_fwd.hpp:
#include
On Tue, Jun 3, 2014 at 2:17 PM, Peter Dimov
Andrey Semashev wrote:
Yes, I had similar idea. I'll try to extract minimal bits of MPL in a separate branch in Core, maybe dropping some workarounds in the process to reduce the amount of code.
Extracting the commonly used parts of MPL such as mpl::bool_, mpl::if_, mpl::and_ will help, although I'd argue that they need to go into module mpl_core, not into module core.
That is ok, if we decide that Boost.Core can depend on Boost.MPL.Core. Otherwise there's not much point in such extraction as MPL doesn't have many dependencies itself.
Andrey Semashev wrote:
That is ok, if we decide that Boost.Core can depend on Boost.MPL.Core.
Because then blank.hpp will depend on mpl_core, and we won't be able to move it into core? True, unfortunately.
Otherwise there's not much point in such extraction as MPL doesn't have many dependencies itself.
On Tue, Jun 3, 2014 at 2:43 PM, Peter Dimov
Andrey Semashev wrote:
That is ok, if we decide that Boost.Core can depend on Boost.MPL.Core.
Because then blank.hpp will depend on mpl_core, and we won't be able to move it into core? True, unfortunately.
Otherwise there's not much point in such extraction as MPL doesn't have many dependencies itself.
Utility will go away as soon as we deal with value_init.hpp. Detail - when detail/endian.hpp is replaced with Predef. Config, PP, StaticAssert and TypeTraits are left. Not so bad already, almost all of them are valid dependencies of Core. TypeTraits might further be replaced with Core.TypeTraits. So what do you think, is it worth to split MPL?
Andrey Semashev wrote:
So what do you think, is it worth to split MPL?
I do think that it will be worth it, although I'm not entirely sure that this needs to be an immediate priority of ours. If we have the core type traits, much of the focus on MPL might go away, although dependencies have their own insidious ways of getting back into the tent. value_init is an example of what happens. You have a library of 147 (say) headers. One of them (for_each.hpp) is affected by a compiler bug. Someone fixes it by including value_init.hpp into it. Now everyone who includes some of the other 146 headers acquires a module-level dependency on whatever hosts value_init. A simple core part is less prone to this dependency creep. I agree that the core type traits will help more, though. Many modules will be able to avoid any contact with MPL if we have those.
John Maddock wrote:
The idea of artificially splitting up type_traits seems pretty abhorrent to be honest.
Based on that description I suspect that I didn't explain myself clearly enough, as I don't believe the refactoring is artificial at all. A new module, core type traits, is created, with its contents corresponding more or less to the standard type traits, minus the integral_constant requirement, plus the requirement to not depend on anything non-core, and hence, minus common_type. This module contains, to take an example, namespace boost { namespace core_type_traits { template<class T> struct is_pointer { BOOST_STATIC_CONSTANT( bool, value = false ); }; template<class T> struct is_pointer { BOOST_STATIC_CONSTANT( bool, value = true ); }; } // core_type_traits } // boost (workarounds omitted for brevity.) A valid alternative implementation for it is: namespace boost { namespace core_type_traits { using std::is_pointer; } } boost::is_pointer is not removed from type traits. It is just implemented as: namespace boost { template<class T> struct is_pointer: public mpl::bool_< core_type_traits::is_pointer<T>::value > { BOOST_MPL_AUX_LAMBDA_SUPPORT(1,is_pointer,(T)) }; BOOST_TT_AUX_TEMPLATE_ARITY_SPEC(1,is_pointer) } // boost That is, the MPL support is decoupled from the actual logic. Currently, is_pointer happens to be implemented in exactly such a way, except that the core part is called is_pointer_impl. Now a type can specialize core_type_traits::is_pod so that ::value is true and all existing uses of boost::is_pod will see mpl::true_. I honestly don't see what do you consider artificial in such an approach. It's basic layering.
On Tue, Jun 3, 2014 at 1:46 PM, Peter Dimov
template<class T> struct is_pointer { BOOST_STATIC_CONSTANT( bool, value = false ); };
template<class T> struct is_pointer
Grrr.
template<class T> struct is_pointer< T* >
{ BOOST_STATIC_CONSTANT( bool, value = true ); };
I think that similarly to Boost.MPL.Core such fundamental type traits could comprise Boost.TypeTraits.Core. Pulling them to Boost.Core seems as bad as pulling Boost.MPL.Core. Actually, it's starting to look like Boost.Core is begging for submodules already: Boost.Core.MPL, Boost.Core.TypeTraits. I suspect at some point we'll be discussing Boost.Core.Preprocessor, too. :)
Andrey Semashev wrote:
I think that similarly to Boost.MPL.Core such fundamental type traits could comprise Boost.TypeTraits.Core. Pulling them to Boost.Core seems as bad as pulling Boost.MPL.Core.
Er. I did say "A new module, core type traits, is created..." Then, a few lines down, I did write namespace core_type_traits
On Tue, Jun 3, 2014 at 2:48 PM, Peter Dimov
Andrey Semashev wrote:
I think that similarly to Boost.MPL.Core such fundamental type traits could comprise Boost.TypeTraits.Core. Pulling them to Boost.Core seems as bad as pulling Boost.MPL.Core.
Er.
I did say
"A new module, core type traits, is created..."
Oh, sorry. I must have skipped right down to the code.
participants (7)
-
Andrey Semashev
-
Felipe Magno de Almeida
-
John Maddock
-
Jonathan Wakely
-
Peter Dimov
-
Rob Stewart
-
Vicente J. Botet Escriba