From dc26209039fc6421920ae02fe1570efff7c1df15 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Mon, 12 Dec 2016 18:11:49 -0500 Subject: [PATCH 1/2] 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 535516b370..f0aa6e16c7 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1004,23 +1004,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 @@ -1080,7 +1081,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 @@ class unpacking_collector { /// 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 712c1a5d62..03e58aa949 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -345,8 +345,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 @@ -356,6 +365,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...); }; @@ -377,35 +409,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 0a1208e16d..064587839e 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -42,9 +42,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 1db9efb8ce..afdb656ac5 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 @@ class class_ : public detail::generic_type { 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 2b49ecfc96..9753958664 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -410,12 +410,10 @@ class args_proxy : public handle { 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 @@ class dict : public object { 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 ef9950ebb1..d1d45e2c0b 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 b9b56c15d8..ce0b33257d 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; From 7e0d3eb000efe775a8959091ec0a0aab3048f6c1 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Tue, 13 Dec 2016 12:23:12 -0500 Subject: [PATCH 2/2] Add simple any_of/all_of implementation for C++17 --- include/pybind11/common.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/pybind11/common.h b/include/pybind11/common.h index 03e58aa949..eb34efff1a 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -374,7 +374,10 @@ template using negation = bool_constant; #endif /// Compile-time all/any/none of that check the ::value of all template types -#if !defined(_MSC_VER) +#ifdef PYBIND11_CPP17 +template using all_of = bool_constant<(Ts::value && ...)>; +template using any_of = bool_constant<(Ts::value || ...)>; +#elif !defined(_MSC_VER) template struct bools {}; template using all_of = std::is_same< bools,