From fa5d05e15de402eba3f6142fd3933f56b07d7a33 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Mon, 12 Dec 2016 18:11:49 -0500 Subject: [PATCH] Change all_of_t/any_of_t to all_of/any_of, add none_of This replaces the current `all_of_t` with `all_of`, with previous use of `all_of_t` becoming `all_of...>` (and similarly for `any_of_t`). It also adds a `none_of`, a shortcut for `negation>`. 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. --- include/pybind11/cast.h | 37 +++++++++++------------ include/pybind11/common.h | 58 +++++++++++++++++++++---------------- include/pybind11/eigen.h | 7 +++-- include/pybind11/pybind11.h | 11 +++---- include/pybind11/pytypes.h | 10 +++---- include/pybind11/stl_bind.h | 4 +-- tests/test_stl_binders.cpp | 5 ++++ 7 files changed, 71 insertions(+), 61 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 6edb11041..8e9559ced 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1003,23 +1003,24 @@ class type_caster::value>> : public pyobject_caste // - 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 // If any of the above are not satisfied, we fall back to copying. -template struct move_is_plain_type : std::false_type {}; -template struct move_is_plain_type::value && !std::is_pointer::value && !std::is_reference::value && !std::is_const::value - >> : std::true_type { }; +template using move_is_plain_type = none_of< + std::is_void, std::is_pointer, std::is_reference, std::is_const +>; template struct move_always : std::false_type {}; -template struct move_always::value && - !std::is_copy_constructible::value && std::is_move_constructible::value && - std::is_same>().operator T&()), T&>::value - >> : std::true_type { }; +template struct move_always, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; template struct move_if_unreferenced : std::false_type {}; -template struct move_if_unreferenced::value && - !move_always::value && std::is_move_constructible::value && - std::is_same>().operator T&()), T&>::value - >> : std::true_type { }; -template using move_never = std::integral_constant::value && !move_if_unreferenced::value>; +template struct move_if_unreferenced, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template using move_never = none_of, move_if_unreferenced>; // 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 @@ -1079,7 +1080,7 @@ template T handle::cast() const { return pybind11::cast(*this); template <> inline void handle::cast() const { return; } template -detail::enable_if_t::value || detail::move_if_unreferenced::value, T> move(object &&obj) { +detail::enable_if_t::value, T> move(object &&obj) { if (obj.ref_count() > 1) #if defined(NDEBUG) 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 template ::value>> + typename = enable_if_t...>::value>> simple_collector collect_arguments(Args &&...args) { return simple_collector(std::forward(args)...); } /// Collect all arguments, including keywords and unpacking (only instantiated when needed) template ::value>> + typename = enable_if_t...>::value>> unpacking_collector collect_arguments(Args &&...args) { // Following argument order rules for generalized unpacking according to PEP 448 static_assert( diff --git a/include/pybind11/common.h b/include/pybind11/common.h index dcb581075..254fbaddc 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -356,8 +356,17 @@ struct internals { /// Return a reference to the current 'internals' information 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 +using std::enable_if_t; +using std::conditional_t; +#else +template using enable_if_t = typename std::enable_if::type; +template using conditional_t = typename std::conditional::type; +#endif + +/// Index sequences +#if defined(PYBIND11_CPP14) || defined(_MSC_VER) using std::index_sequence; using std::make_index_sequence; #else @@ -367,6 +376,29 @@ template struct make_index_sequence_impl <0, S...> { typedef index_ template using make_index_sequence = typename make_index_sequence_impl::type; #endif +#if defined(PYBIND11_CPP17) || defined(_MSC_VER) +using std::bool_constant; +using std::negation; +#else +template using bool_constant = std::integral_constant; +template using negation = bool_constant; +#endif + +/// Compile-time all/any/none of that check the ::value of all template types +#if !defined(_MSC_VER) +template struct bools {}; +template using all_of = std::is_same< + bools, + bools>; +template using any_of = negation...>>; +#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 using all_of = std::conjunction; +template using any_of = std::disjunction; +#endif +template using none_of = negation>; + /// Strip the class from a method type template struct remove_class { }; template struct remove_class { typedef R type(A...); }; @@ -388,35 +420,11 @@ struct void_type { }; /// Helper template which holds a list of types template struct type_list { }; -/// from __cpp_future__ import (convenient aliases from C++14/17) -template using bool_constant = std::integral_constant; -template using negation = bool_constant; -template using enable_if_t = typename std::enable_if::type; -template using conditional_t = typename std::conditional::type; - /// Compile-time integer sum constexpr size_t constexpr_sum() { return 0; } template 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 class Predicate, typename... Ts> -using count_t = std::integral_constant::value...)>; -#else -// MSVC workaround (2015 Update 3 has issues with some member type aliases and constexpr) -template class Predicate, typename... Ts> struct count_t; -template class Predicate> struct count_t : std::integral_constant {}; -template class Predicate, class T, class... Ts> -struct count_t : std::integral_constant::value + count_t::value> {}; -#endif - -/// Return true if all/any Ts satify Predicate -template class Predicate, typename... Ts> -using all_of_t = bool_constant<(count_t::value == sizeof...(Ts))>; -template class Predicate, typename... Ts> -using any_of_t = bool_constant<(count_t::value > 0)>; - // Extracts the first type from the template parameter pack matching the predicate, or Default if none match. template class Predicate, class Default, class... Ts> struct first_of; template class Predicate, class Default> struct first_of { diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index 72e876a8a..d9a3aa9c5 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -41,9 +41,10 @@ template using is_eigen_ref = is_template_base_of using is_eigen_base = bool_constant< - is_template_base_of::value - && !is_eigen_dense::value && !is_eigen_sparse::value +template using is_eigen_base = all_of< + is_template_base_of, + negation>, + negation> >; template diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 1492cd290..e01289589 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -924,12 +924,9 @@ class class_ : public detail::generic_type { template using is_holder = detail::is_holder_type; template using is_subtype = detail::bool_constant::value && !std::is_same::value>; template using is_base = detail::bool_constant::value && !std::is_same::value>; - template using is_valid_class_option = - detail::bool_constant< - is_holder::value || - is_subtype::value || - is_base::value - >; + // struct instead of using here to help MSVC: + template struct is_valid_class_option : + detail::any_of, is_subtype, is_base> {}; public: using type = type_; @@ -938,7 +935,7 @@ public: using holder_type = detail::first_of_t, options...>; using instance_type = detail::instance; - static_assert(detail::all_of_t::value, + static_assert(detail::all_of...>::value, "Unknown/invalid class_ template parameters provided"); PYBIND11_OBJECT(class_, generic_type, PyType_Check) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 2b49ecfc9..975395866 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -410,12 +410,10 @@ public: template using is_keyword = std::is_base_of; template using is_s_unpacking = std::is_same; // * unpacking template using is_ds_unpacking = std::is_same; // ** unpacking -template using is_positional = bool_constant< - !is_keyword::value && !is_s_unpacking::value && !is_ds_unpacking::value ->; -template using is_keyword_or_ds = bool_constant< - is_keyword::value || is_ds_unpacking::value +template using is_positional = none_of< + is_keyword, is_s_unpacking, is_ds_unpacking >; +template using is_keyword_or_ds = any_of, is_ds_unpacking>; // Call argument collector forward declarations template @@ -754,7 +752,7 @@ public: if (!m_ptr) pybind11_fail("Could not allocate dict object!"); } template ::value>, + typename = detail::enable_if_t...>::value>, // MSVC workaround: it can't compile an out-of-line definition, so defer the collector typename collector = detail::deferred_t, Args...>> explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index ef9950ebb..d1d45e2c0 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -253,8 +253,8 @@ void vector_modifiers(enable_if_t), // we have to access by copying; otherwise we return by reference. -template using vector_needs_copy = bool_constant< - !std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>::value>; +template using vector_needs_copy = negation< + std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; // The usual case: access and iterate by reference template diff --git a/tests/test_stl_binders.cpp b/tests/test_stl_binders.cpp index b9b56c15d..ce0b33257 100644 --- a/tests/test_stl_binders.cpp +++ b/tests/test_stl_binders.cpp @@ -14,6 +14,11 @@ #include #include +#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 { public: El() = delete;