Skip to content

Commit fa5d05e

Browse files
jagermanwjakob
authored andcommitted
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.
1 parent b11b144 commit fa5d05e

File tree

7 files changed

+71
-61
lines changed

7 files changed

+71
-61
lines changed

include/pybind11/cast.h

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,23 +1003,24 @@ class type_caster<T, enable_if_t<is_pyobject<T>::value>> : public pyobject_caste
10031003
// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it
10041004
// must have ref_count() == 1)h
10051005
// If any of the above are not satisfied, we fall back to copying.
1006-
template <typename T, typename SFINAE = void> struct move_is_plain_type : std::false_type {};
1007-
template <typename T> struct move_is_plain_type<T, enable_if_t<
1008-
!std::is_void<T>::value && !std::is_pointer<T>::value && !std::is_reference<T>::value && !std::is_const<T>::value
1009-
>> : std::true_type { };
1006+
template <typename T> using move_is_plain_type = none_of<
1007+
std::is_void<T>, std::is_pointer<T>, std::is_reference<T>, std::is_const<T>
1008+
>;
10101009
template <typename T, typename SFINAE = void> struct move_always : std::false_type {};
1011-
template <typename T> struct move_always<T, enable_if_t<
1012-
move_is_plain_type<T>::value &&
1013-
!std::is_copy_constructible<T>::value && std::is_move_constructible<T>::value &&
1014-
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>::value
1015-
>> : std::true_type { };
1010+
template <typename T> struct move_always<T, enable_if_t<all_of<
1011+
move_is_plain_type<T>,
1012+
negation<std::is_copy_constructible<T>>,
1013+
std::is_move_constructible<T>,
1014+
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>
1015+
>::value>> : std::true_type {};
10161016
template <typename T, typename SFINAE = void> struct move_if_unreferenced : std::false_type {};
1017-
template <typename T> struct move_if_unreferenced<T, enable_if_t<
1018-
move_is_plain_type<T>::value &&
1019-
!move_always<T>::value && std::is_move_constructible<T>::value &&
1020-
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>::value
1021-
>> : std::true_type { };
1022-
template <typename T> using move_never = std::integral_constant<bool, !move_always<T>::value && !move_if_unreferenced<T>::value>;
1017+
template <typename T> struct move_if_unreferenced<T, enable_if_t<all_of<
1018+
move_is_plain_type<T>,
1019+
negation<move_always<T>>,
1020+
std::is_move_constructible<T>,
1021+
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>
1022+
>::value>> : std::true_type {};
1023+
template <typename T> using move_never = none_of<move_always<T>, move_if_unreferenced<T>>;
10231024

10241025
// Detect whether returning a `type` from a cast on type's type_caster is going to result in a
10251026
// 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);
10791080
template <> inline void handle::cast() const { return; }
10801081

10811082
template <typename T>
1082-
detail::enable_if_t<detail::move_always<T>::value || detail::move_if_unreferenced<T>::value, T> move(object &&obj) {
1083+
detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
10831084
if (obj.ref_count() > 1)
10841085
#if defined(NDEBUG)
10851086
throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references"
@@ -1427,14 +1428,14 @@ class unpacking_collector {
14271428

14281429
/// Collect only positional arguments for a Python function call
14291430
template <return_value_policy policy, typename... Args,
1430-
typename = enable_if_t<all_of_t<is_positional, Args...>::value>>
1431+
typename = enable_if_t<all_of<is_positional<Args>...>::value>>
14311432
simple_collector<policy> collect_arguments(Args &&...args) {
14321433
return simple_collector<policy>(std::forward<Args>(args)...);
14331434
}
14341435

14351436
/// Collect all arguments, including keywords and unpacking (only instantiated when needed)
14361437
template <return_value_policy policy, typename... Args,
1437-
typename = enable_if_t<!all_of_t<is_positional, Args...>::value>>
1438+
typename = enable_if_t<!all_of<is_positional<Args>...>::value>>
14381439
unpacking_collector<policy> collect_arguments(Args &&...args) {
14391440
// Following argument order rules for generalized unpacking according to PEP 448
14401441
static_assert(

include/pybind11/common.h

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -356,8 +356,17 @@ struct internals {
356356
/// Return a reference to the current 'internals' information
357357
inline internals &get_internals();
358358

359-
/// Index sequence for convenient template metaprogramming involving tuples
359+
/// from __cpp_future__ import (convenient aliases from C++14/17)
360360
#ifdef PYBIND11_CPP14
361+
using std::enable_if_t;
362+
using std::conditional_t;
363+
#else
364+
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
365+
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
366+
#endif
367+
368+
/// Index sequences
369+
#if defined(PYBIND11_CPP14) || defined(_MSC_VER)
361370
using std::index_sequence;
362371
using std::make_index_sequence;
363372
#else
@@ -367,6 +376,29 @@ template<size_t ...S> struct make_index_sequence_impl <0, S...> { typedef index_
367376
template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type;
368377
#endif
369378

379+
#if defined(PYBIND11_CPP17) || defined(_MSC_VER)
380+
using std::bool_constant;
381+
using std::negation;
382+
#else
383+
template <bool B> using bool_constant = std::integral_constant<bool, B>;
384+
template <class T> using negation = bool_constant<!T::value>;
385+
#endif
386+
387+
/// Compile-time all/any/none of that check the ::value of all template types
388+
#if !defined(_MSC_VER)
389+
template <bool...> struct bools {};
390+
template <class... Ts> using all_of = std::is_same<
391+
bools<Ts::value..., true>,
392+
bools<true, Ts::value...>>;
393+
template <class... Ts> using any_of = negation<all_of<negation<Ts>...>>;
394+
#else
395+
// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit
396+
// at a slight loss of compilation efficiency).
397+
template <class... Ts> using all_of = std::conjunction<Ts...>;
398+
template <class... Ts> using any_of = std::disjunction<Ts...>;
399+
#endif
400+
template <class... Ts> using none_of = negation<any_of<Ts...>>;
401+
370402
/// Strip the class from a method type
371403
template <typename T> struct remove_class { };
372404
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); };
@@ -388,35 +420,11 @@ struct void_type { };
388420
/// Helper template which holds a list of types
389421
template <typename...> struct type_list { };
390422

391-
/// from __cpp_future__ import (convenient aliases from C++14/17)
392-
template <bool B> using bool_constant = std::integral_constant<bool, B>;
393-
template <class T> using negation = bool_constant<!T::value>;
394-
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
395-
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
396-
397423
/// Compile-time integer sum
398424
constexpr size_t constexpr_sum() { return 0; }
399425
template <typename T, typename... Ts>
400426
constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); }
401427

402-
// Counts the number of types in the template parameter pack matching the predicate
403-
#if !defined(_MSC_VER)
404-
template <template<typename> class Predicate, typename... Ts>
405-
using count_t = std::integral_constant<size_t, constexpr_sum(Predicate<Ts>::value...)>;
406-
#else
407-
// MSVC workaround (2015 Update 3 has issues with some member type aliases and constexpr)
408-
template <template<typename> class Predicate, typename... Ts> struct count_t;
409-
template <template<typename> class Predicate> struct count_t<Predicate> : std::integral_constant<size_t, 0> {};
410-
template <template<typename> class Predicate, class T, class... Ts>
411-
struct count_t<Predicate, T, Ts...> : std::integral_constant<size_t, Predicate<T>::value + count_t<Predicate, Ts...>::value> {};
412-
#endif
413-
414-
/// Return true if all/any Ts satify Predicate<T>
415-
template <template<typename> class Predicate, typename... Ts>
416-
using all_of_t = bool_constant<(count_t<Predicate, Ts...>::value == sizeof...(Ts))>;
417-
template <template<typename> class Predicate, typename... Ts>
418-
using any_of_t = bool_constant<(count_t<Predicate, Ts...>::value > 0)>;
419-
420428
// Extracts the first type from the template parameter pack matching the predicate, or Default if none match.
421429
template <template<class> class Predicate, class Default, class... Ts> struct first_of;
422430
template <template<class> class Predicate, class Default> struct first_of<Predicate, Default> {

include/pybind11/eigen.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ template <typename T> using is_eigen_ref = is_template_base_of<Eigen::RefBase, T
4141
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
4242
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
4343
// SelfAdjointView fall into this category.
44-
template <typename T> using is_eigen_base = bool_constant<
45-
is_template_base_of<Eigen::EigenBase, T>::value
46-
&& !is_eigen_dense<T>::value && !is_eigen_sparse<T>::value
44+
template <typename T> using is_eigen_base = all_of<
45+
is_template_base_of<Eigen::EigenBase, T>,
46+
negation<is_eigen_dense<T>>,
47+
negation<is_eigen_sparse<T>>
4748
>;
4849

4950
template<typename Type>

include/pybind11/pybind11.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -924,12 +924,9 @@ class class_ : public detail::generic_type {
924924
template <typename T> using is_holder = detail::is_holder_type<type_, T>;
925925
template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>;
926926
template <typename T> using is_base = detail::bool_constant<std::is_base_of<T, type_>::value && !std::is_same<T, type_>::value>;
927-
template <typename T> using is_valid_class_option =
928-
detail::bool_constant<
929-
is_holder<T>::value ||
930-
is_subtype<T>::value ||
931-
is_base<T>::value
932-
>;
927+
// struct instead of using here to help MSVC:
928+
template <typename T> struct is_valid_class_option :
929+
detail::any_of<is_holder<T>, is_subtype<T>, is_base<T>> {};
933930

934931
public:
935932
using type = type_;
@@ -938,7 +935,7 @@ class class_ : public detail::generic_type {
938935
using holder_type = detail::first_of_t<is_holder, std::unique_ptr<type>, options...>;
939936
using instance_type = detail::instance<type, holder_type>;
940937

941-
static_assert(detail::all_of_t<is_valid_class_option, options...>::value,
938+
static_assert(detail::all_of<is_valid_class_option<options>...>::value,
942939
"Unknown/invalid class_ template parameters provided");
943940

944941
PYBIND11_OBJECT(class_, generic_type, PyType_Check)

include/pybind11/pytypes.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,10 @@ class args_proxy : public handle {
410410
template <typename T> using is_keyword = std::is_base_of<arg, T>;
411411
template <typename T> using is_s_unpacking = std::is_same<args_proxy, T>; // * unpacking
412412
template <typename T> using is_ds_unpacking = std::is_same<kwargs_proxy, T>; // ** unpacking
413-
template <typename T> using is_positional = bool_constant<
414-
!is_keyword<T>::value && !is_s_unpacking<T>::value && !is_ds_unpacking<T>::value
415-
>;
416-
template <typename T> using is_keyword_or_ds = bool_constant<
417-
is_keyword<T>::value || is_ds_unpacking<T>::value
413+
template <typename T> using is_positional = none_of<
414+
is_keyword<T>, is_s_unpacking<T>, is_ds_unpacking<T>
418415
>;
416+
template <typename T> using is_keyword_or_ds = any_of<is_keyword<T>, is_ds_unpacking<T>>;
419417

420418
// Call argument collector forward declarations
421419
template <return_value_policy policy = return_value_policy::automatic_reference>
@@ -754,7 +752,7 @@ class dict : public object {
754752
if (!m_ptr) pybind11_fail("Could not allocate dict object!");
755753
}
756754
template <typename... Args,
757-
typename = detail::enable_if_t<detail::all_of_t<detail::is_keyword_or_ds, Args...>::value>,
755+
typename = detail::enable_if_t<detail::all_of<detail::is_keyword_or_ds<Args>...>::value>,
758756
// MSVC workaround: it can't compile an out-of-line definition, so defer the collector
759757
typename collector = detail::deferred_t<detail::unpacking_collector<>, Args...>>
760758
explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }

include/pybind11/stl_bind.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ void vector_modifiers(enable_if_t<std::is_copy_constructible<typename Vector::va
253253

254254
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
255255
// we have to access by copying; otherwise we return by reference.
256-
template <typename Vector> using vector_needs_copy = bool_constant<
257-
!std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>::value>;
256+
template <typename Vector> using vector_needs_copy = negation<
257+
std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>>;
258258

259259
// The usual case: access and iterate by reference
260260
template <typename Vector, typename Class_>

tests/test_stl_binders.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
#include <deque>
1515
#include <unordered_map>
1616

17+
#ifdef _MSC_VER
18+
// We get some really long type names here which causes MSVC to emit warnings
19+
# pragma warning(disable: 4503) // warning C4503: decorated name length exceeded, name was truncated
20+
#endif
21+
1722
class El {
1823
public:
1924
El() = delete;

0 commit comments

Comments
 (0)