Change all_of_t/any_of_t to all_of/any_of, add none_of

This replaces the current `all_of_t<Pred, Ts...>` with `all_of<Ts...>`,
with previous use of `all_of_t<Pred, Ts...>` becoming
`all_of<Pred<Ts>...>` (and similarly for `any_of_t`).  It also adds a
`none_of<Ts...>`, a shortcut for `negation<any_of<Ts...>>`.

This allows `all_of` and `any_of` to be used a bit more flexible, e.g.
in cases where several predicates need to be tested for the same type
instead of the same predicate for multiple types.

This commit replaces the implementation with a more efficient version
for non-MSVC.  For MSVC, this changes the workaround to use the
built-in, recursive std::conjunction/std::disjunction instead.

This also removes the `count_t` since `any_of_t` and `all_of_t` were the
only things using it.

This commit also rearranges some of the future std imports to use actual
`std` implementations for C++14/17 features when under the appropriate
compiler mode, as we were already doing for a few things (like
index_sequence).  Most of these aren't saving much (the implementation
for enable_if_t, for example, is trivial), but I think it makes the
intention of the code instantly clear.  It also enables MSVC's native
std::index_sequence support.
This commit is contained in:
Jason Rhinelander 2016-12-12 18:11:49 -05:00 committed by Wenzel Jakob
parent b11b144603
commit fa5d05e15d
7 changed files with 71 additions and 61 deletions

View File

@ -1003,23 +1003,24 @@ class type_caster<T, enable_if_t<is_pyobject<T>::value>> : public pyobject_caste
// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it // - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it
// must have ref_count() == 1)h // must have ref_count() == 1)h
// If any of the above are not satisfied, we fall back to copying. // If any of the above are not satisfied, we fall back to copying.
template <typename T, typename SFINAE = void> struct move_is_plain_type : std::false_type {}; template <typename T> using move_is_plain_type = none_of<
template <typename T> struct move_is_plain_type<T, enable_if_t< std::is_void<T>, std::is_pointer<T>, std::is_reference<T>, std::is_const<T>
!std::is_void<T>::value && !std::is_pointer<T>::value && !std::is_reference<T>::value && !std::is_const<T>::value >;
>> : std::true_type { };
template <typename T, typename SFINAE = void> struct move_always : std::false_type {}; template <typename T, typename SFINAE = void> struct move_always : std::false_type {};
template <typename T> struct move_always<T, enable_if_t< template <typename T> struct move_always<T, enable_if_t<all_of<
move_is_plain_type<T>::value && move_is_plain_type<T>,
!std::is_copy_constructible<T>::value && std::is_move_constructible<T>::value && negation<std::is_copy_constructible<T>>,
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>::value std::is_move_constructible<T>,
>> : std::true_type { }; std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>
>::value>> : std::true_type {};
template <typename T, typename SFINAE = void> struct move_if_unreferenced : std::false_type {}; template <typename T, typename SFINAE = void> struct move_if_unreferenced : std::false_type {};
template <typename T> struct move_if_unreferenced<T, enable_if_t< template <typename T> struct move_if_unreferenced<T, enable_if_t<all_of<
move_is_plain_type<T>::value && move_is_plain_type<T>,
!move_always<T>::value && std::is_move_constructible<T>::value && negation<move_always<T>>,
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>::value std::is_move_constructible<T>,
>> : std::true_type { }; std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>
template <typename T> using move_never = std::integral_constant<bool, !move_always<T>::value && !move_if_unreferenced<T>::value>; >::value>> : std::true_type {};
template <typename T> using move_never = none_of<move_always<T>, move_if_unreferenced<T>>;
// Detect whether returning a `type` from a cast on type's type_caster is going to result in a // Detect whether returning a `type` from a cast on type's type_caster is going to result in a
// reference or pointer to a local variable of the type_caster. Basically, only // reference or pointer to a local variable of the type_caster. Basically, only
@ -1079,7 +1080,7 @@ template <typename T> T handle::cast() const { return pybind11::cast<T>(*this);
template <> inline void handle::cast() const { return; } template <> inline void handle::cast() const { return; }
template <typename T> template <typename T>
detail::enable_if_t<detail::move_always<T>::value || detail::move_if_unreferenced<T>::value, T> move(object &&obj) { detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
if (obj.ref_count() > 1) if (obj.ref_count() > 1)
#if defined(NDEBUG) #if defined(NDEBUG)
throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references"
@ -1427,14 +1428,14 @@ private:
/// Collect only positional arguments for a Python function call /// Collect only positional arguments for a Python function call
template <return_value_policy policy, typename... Args, template <return_value_policy policy, typename... Args,
typename = enable_if_t<all_of_t<is_positional, Args...>::value>> typename = enable_if_t<all_of<is_positional<Args>...>::value>>
simple_collector<policy> collect_arguments(Args &&...args) { simple_collector<policy> collect_arguments(Args &&...args) {
return simple_collector<policy>(std::forward<Args>(args)...); return simple_collector<policy>(std::forward<Args>(args)...);
} }
/// Collect all arguments, including keywords and unpacking (only instantiated when needed) /// Collect all arguments, including keywords and unpacking (only instantiated when needed)
template <return_value_policy policy, typename... Args, template <return_value_policy policy, typename... Args,
typename = enable_if_t<!all_of_t<is_positional, Args...>::value>> typename = enable_if_t<!all_of<is_positional<Args>...>::value>>
unpacking_collector<policy> collect_arguments(Args &&...args) { unpacking_collector<policy> collect_arguments(Args &&...args) {
// Following argument order rules for generalized unpacking according to PEP 448 // Following argument order rules for generalized unpacking according to PEP 448
static_assert( static_assert(

View File

@ -356,8 +356,17 @@ struct internals {
/// Return a reference to the current 'internals' information /// Return a reference to the current 'internals' information
inline internals &get_internals(); inline internals &get_internals();
/// Index sequence for convenient template metaprogramming involving tuples /// from __cpp_future__ import (convenient aliases from C++14/17)
#ifdef PYBIND11_CPP14 #ifdef PYBIND11_CPP14
using std::enable_if_t;
using std::conditional_t;
#else
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
#endif
/// Index sequences
#if defined(PYBIND11_CPP14) || defined(_MSC_VER)
using std::index_sequence; using std::index_sequence;
using std::make_index_sequence; using std::make_index_sequence;
#else #else
@ -367,6 +376,29 @@ template<size_t ...S> struct make_index_sequence_impl <0, S...> { typedef index_
template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type; template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type;
#endif #endif
#if defined(PYBIND11_CPP17) || defined(_MSC_VER)
using std::bool_constant;
using std::negation;
#else
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <class T> using negation = bool_constant<!T::value>;
#endif
/// Compile-time all/any/none of that check the ::value of all template types
#if !defined(_MSC_VER)
template <bool...> struct bools {};
template <class... Ts> using all_of = std::is_same<
bools<Ts::value..., true>,
bools<true, Ts::value...>>;
template <class... Ts> using any_of = negation<all_of<negation<Ts>...>>;
#else
// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit
// at a slight loss of compilation efficiency).
template <class... Ts> using all_of = std::conjunction<Ts...>;
template <class... Ts> using any_of = std::disjunction<Ts...>;
#endif
template <class... Ts> using none_of = negation<any_of<Ts...>>;
/// Strip the class from a method type /// Strip the class from a method type
template <typename T> struct remove_class { }; template <typename T> struct remove_class { };
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); }; template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); };
@ -388,35 +420,11 @@ struct void_type { };
/// Helper template which holds a list of types /// Helper template which holds a list of types
template <typename...> struct type_list { }; template <typename...> struct type_list { };
/// from __cpp_future__ import (convenient aliases from C++14/17)
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <class T> using negation = bool_constant<!T::value>;
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
/// Compile-time integer sum /// Compile-time integer sum
constexpr size_t constexpr_sum() { return 0; } constexpr size_t constexpr_sum() { return 0; }
template <typename T, typename... Ts> template <typename T, typename... Ts>
constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); }
// Counts the number of types in the template parameter pack matching the predicate
#if !defined(_MSC_VER)
template <template<typename> class Predicate, typename... Ts>
using count_t = std::integral_constant<size_t, constexpr_sum(Predicate<Ts>::value...)>;
#else
// MSVC workaround (2015 Update 3 has issues with some member type aliases and constexpr)
template <template<typename> class Predicate, typename... Ts> struct count_t;
template <template<typename> class Predicate> struct count_t<Predicate> : std::integral_constant<size_t, 0> {};
template <template<typename> class Predicate, class T, class... Ts>
struct count_t<Predicate, T, Ts...> : std::integral_constant<size_t, Predicate<T>::value + count_t<Predicate, Ts...>::value> {};
#endif
/// Return true if all/any Ts satify Predicate<T>
template <template<typename> class Predicate, typename... Ts>
using all_of_t = bool_constant<(count_t<Predicate, Ts...>::value == sizeof...(Ts))>;
template <template<typename> class Predicate, typename... Ts>
using any_of_t = bool_constant<(count_t<Predicate, Ts...>::value > 0)>;
// Extracts the first type from the template parameter pack matching the predicate, or Default if none match. // Extracts the first type from the template parameter pack matching the predicate, or Default if none match.
template <template<class> class Predicate, class Default, class... Ts> struct first_of; template <template<class> class Predicate, class Default, class... Ts> struct first_of;
template <template<class> class Predicate, class Default> struct first_of<Predicate, Default> { template <template<class> class Predicate, class Default> struct first_of<Predicate, Default> {

View File

@ -41,9 +41,10 @@ template <typename T> using is_eigen_ref = is_template_base_of<Eigen::RefBase, T
// basically covers anything that can be assigned to a dense matrix but that don't have a typical // basically covers anything that can be assigned to a dense matrix but that don't have a typical
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and // matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
// SelfAdjointView fall into this category. // SelfAdjointView fall into this category.
template <typename T> using is_eigen_base = bool_constant< template <typename T> using is_eigen_base = all_of<
is_template_base_of<Eigen::EigenBase, T>::value is_template_base_of<Eigen::EigenBase, T>,
&& !is_eigen_dense<T>::value && !is_eigen_sparse<T>::value negation<is_eigen_dense<T>>,
negation<is_eigen_sparse<T>>
>; >;
template<typename Type> template<typename Type>

View File

@ -924,12 +924,9 @@ class class_ : public detail::generic_type {
template <typename T> using is_holder = detail::is_holder_type<type_, T>; template <typename T> using is_holder = detail::is_holder_type<type_, T>;
template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>; template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>;
template <typename T> using is_base = detail::bool_constant<std::is_base_of<T, type_>::value && !std::is_same<T, type_>::value>; template <typename T> using is_base = detail::bool_constant<std::is_base_of<T, type_>::value && !std::is_same<T, type_>::value>;
template <typename T> using is_valid_class_option = // struct instead of using here to help MSVC:
detail::bool_constant< template <typename T> struct is_valid_class_option :
is_holder<T>::value || detail::any_of<is_holder<T>, is_subtype<T>, is_base<T>> {};
is_subtype<T>::value ||
is_base<T>::value
>;
public: public:
using type = type_; using type = type_;
@ -938,7 +935,7 @@ public:
using holder_type = detail::first_of_t<is_holder, std::unique_ptr<type>, options...>; using holder_type = detail::first_of_t<is_holder, std::unique_ptr<type>, options...>;
using instance_type = detail::instance<type, holder_type>; using instance_type = detail::instance<type, holder_type>;
static_assert(detail::all_of_t<is_valid_class_option, options...>::value, static_assert(detail::all_of<is_valid_class_option<options>...>::value,
"Unknown/invalid class_ template parameters provided"); "Unknown/invalid class_ template parameters provided");
PYBIND11_OBJECT(class_, generic_type, PyType_Check) PYBIND11_OBJECT(class_, generic_type, PyType_Check)

View File

@ -410,12 +410,10 @@ public:
template <typename T> using is_keyword = std::is_base_of<arg, T>; template <typename T> using is_keyword = std::is_base_of<arg, T>;
template <typename T> using is_s_unpacking = std::is_same<args_proxy, T>; // * unpacking template <typename T> using is_s_unpacking = std::is_same<args_proxy, T>; // * unpacking
template <typename T> using is_ds_unpacking = std::is_same<kwargs_proxy, T>; // ** unpacking template <typename T> using is_ds_unpacking = std::is_same<kwargs_proxy, T>; // ** unpacking
template <typename T> using is_positional = bool_constant< template <typename T> using is_positional = none_of<
!is_keyword<T>::value && !is_s_unpacking<T>::value && !is_ds_unpacking<T>::value is_keyword<T>, is_s_unpacking<T>, is_ds_unpacking<T>
>;
template <typename T> using is_keyword_or_ds = bool_constant<
is_keyword<T>::value || is_ds_unpacking<T>::value
>; >;
template <typename T> using is_keyword_or_ds = any_of<is_keyword<T>, is_ds_unpacking<T>>;
// Call argument collector forward declarations // Call argument collector forward declarations
template <return_value_policy policy = return_value_policy::automatic_reference> template <return_value_policy policy = return_value_policy::automatic_reference>
@ -754,7 +752,7 @@ public:
if (!m_ptr) pybind11_fail("Could not allocate dict object!"); if (!m_ptr) pybind11_fail("Could not allocate dict object!");
} }
template <typename... Args, template <typename... Args,
typename = detail::enable_if_t<detail::all_of_t<detail::is_keyword_or_ds, Args...>::value>, typename = detail::enable_if_t<detail::all_of<detail::is_keyword_or_ds<Args>...>::value>,
// MSVC workaround: it can't compile an out-of-line definition, so defer the collector // MSVC workaround: it can't compile an out-of-line definition, so defer the collector
typename collector = detail::deferred_t<detail::unpacking_collector<>, Args...>> typename collector = detail::deferred_t<detail::unpacking_collector<>, Args...>>
explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { } explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }

View File

@ -253,8 +253,8 @@ void vector_modifiers(enable_if_t<std::is_copy_constructible<typename Vector::va
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>), // If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
// we have to access by copying; otherwise we return by reference. // we have to access by copying; otherwise we return by reference.
template <typename Vector> using vector_needs_copy = bool_constant< template <typename Vector> using vector_needs_copy = negation<
!std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>::value>; std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>>;
// The usual case: access and iterate by reference // The usual case: access and iterate by reference
template <typename Vector, typename Class_> template <typename Vector, typename Class_>

View File

@ -14,6 +14,11 @@
#include <deque> #include <deque>
#include <unordered_map> #include <unordered_map>
#ifdef _MSC_VER
// We get some really long type names here which causes MSVC to emit warnings
# pragma warning(disable: 4503) // warning C4503: decorated name length exceeded, name was truncated
#endif
class El { class El {
public: public:
El() = delete; El() = delete;