Skip to content

Commit 8803916

Browse files
committed
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. Since this introduces a slightly different implementation when compiling under C++17, I've also added a travis-ci build to compile in C++17 mode under gcc 6. (I tried adding an experimental build with a recent gcc 7 snapshot, but pybind triggers an ICE in c++17 mode with the current g++ snapshot, so for now this is still compiled under g++-6.)
1 parent 76e993a commit 8803916

File tree

8 files changed

+82
-70
lines changed

8 files changed

+82
-70
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ matrix:
2020
- sudo: true
2121
services: docker
2222
env: PYTHON=3.5 CPP=14 GCC=6 DEBUG=1
23+
- sudo: true
24+
services: docker
25+
env: PYTHON=3.5 CPP=17 GCC=6
2326
- os: osx
2427
osx_image: xcode7.3
2528
env: PYTHON=2.7 CPP=14 CLANG

include/pybind11/cast.h

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

10251026
// Detect whether returning a `type` from a cast on type's type_caster is going to result in a
10261027
// reference or pointer to a local variable of the type_caster. Basically, only
10271028
// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe;
10281029
// everything else returns a reference/pointer to a local variable.
1029-
template <typename type> using cast_is_temporary_value_reference = bool_constant<
1030-
(std::is_reference<type>::value || std::is_pointer<type>::value) &&
1031-
!std::is_base_of<type_caster_generic, make_caster<type>>::value
1030+
template <typename type> using cast_is_temporary_value_reference = all_of<
1031+
any_of<std::is_reference<type>, std::is_pointer<type>>,
1032+
negation<std::is_base_of<type_caster_generic, make_caster<type>>>
10321033
>;
10331034

10341035
// Basic python -> C++ casting; throws if casting fails
@@ -1080,7 +1081,7 @@ template <typename T> T handle::cast() const { return pybind11::cast<T>(*this);
10801081
template <> inline void handle::cast() const { return; }
10811082

10821083
template <typename T>
1083-
detail::enable_if_t<detail::move_always<T>::value || detail::move_if_unreferenced<T>::value, T> move(object &&obj) {
1084+
detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
10841085
if (obj.ref_count() > 1)
10851086
#if defined(NDEBUG)
10861087
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
@@ -345,8 +345,17 @@ struct internals {
345345
/// Return a reference to the current 'internals' information
346346
inline internals &get_internals();
347347

348-
/// Index sequence for convenient template metaprogramming involving tuples
348+
/// from __cpp_future__ import (convenient aliases from C++14/17)
349349
#ifdef PYBIND11_CPP14
350+
using std::enable_if_t;
351+
using std::conditional_t;
352+
#else
353+
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
354+
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
355+
#endif
356+
357+
/// Index sequences
358+
#if defined(PYBIND11_CPP14) || defined(_MSC_VER)
350359
using std::index_sequence;
351360
using std::make_index_sequence;
352361
#else
@@ -356,6 +365,29 @@ template<size_t ...S> struct make_index_sequence_impl <0, S...> { typedef index_
356365
template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type;
357366
#endif
358367

368+
#if defined(PYBIND11_CPP17) || defined(_MSC_VER)
369+
using std::bool_constant;
370+
using std::negation;
371+
#else
372+
template <bool B> using bool_constant = std::integral_constant<bool, B>;
373+
template <class T> using negation = bool_constant<!T::value>;
374+
#endif
375+
376+
/// Compile-time all/any/none of that check the ::value of all template types
377+
#if !defined(_MSC_VER)
378+
template <bool...> struct bools {};
379+
template <class... Ts> using all_of = std::is_same<
380+
bools<Ts::value..., true>,
381+
bools<true, Ts::value...>>;
382+
template <class... Ts> using any_of = negation<all_of<negation<Ts>...>>;
383+
#else
384+
// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit
385+
// at a slight loss of compilation efficiency).
386+
template <class... Ts> using all_of = std::conjunction<Ts...>;
387+
template <class... Ts> using any_of = std::disjunction<Ts...>;
388+
#endif
389+
template <class... Ts> using none_of = negation<any_of<Ts...>>;
390+
359391
/// Strip the class from a method type
360392
template <typename T> struct remove_class { };
361393
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); };
@@ -377,35 +409,11 @@ struct void_type { };
377409
/// Helper template which holds a list of types
378410
template <typename...> struct type_list { };
379411

380-
/// from __cpp_future__ import (convenient aliases from C++14/17)
381-
template <bool B> using bool_constant = std::integral_constant<bool, B>;
382-
template <class T> using negation = bool_constant<!T::value>;
383-
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
384-
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
385-
386412
/// Compile-time integer sum
387413
constexpr size_t constexpr_sum() { return 0; }
388414
template <typename T, typename... Ts>
389415
constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); }
390416

391-
// Counts the number of types in the template parameter pack matching the predicate
392-
#if !defined(_MSC_VER)
393-
template <template<typename> class Predicate, typename... Ts>
394-
using count_t = std::integral_constant<size_t, constexpr_sum(Predicate<Ts>::value...)>;
395-
#else
396-
// MSVC workaround (2015 Update 3 has issues with some member type aliases and constexpr)
397-
template <template<typename> class Predicate, typename... Ts> struct count_t;
398-
template <template<typename> class Predicate> struct count_t<Predicate> : std::integral_constant<size_t, 0> {};
399-
template <template<typename> class Predicate, class T, class... Ts>
400-
struct count_t<Predicate, T, Ts...> : std::integral_constant<size_t, Predicate<T>::value + count_t<Predicate, Ts...>::value> {};
401-
#endif
402-
403-
/// Return true if all/any Ts satify Predicate<T>
404-
template <template<typename> class Predicate, typename... Ts>
405-
using all_of_t = bool_constant<(count_t<Predicate, Ts...>::value == sizeof...(Ts))>;
406-
template <template<typename> class Predicate, typename... Ts>
407-
using any_of_t = bool_constant<(count_t<Predicate, Ts...>::value > 0)>;
408-
409417
// Extracts the first type from the template parameter pack matching the predicate, or Default if none match.
410418
template <template<class> class Predicate, class Default, class... Ts> struct first_of;
411419
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
@@ -42,9 +42,10 @@ template <typename T> using is_eigen_ref = is_template_base_of<Eigen::RefBase, T
4242
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
4343
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
4444
// SelfAdjointView fall into this category.
45-
template <typename T> using is_eigen_base = bool_constant<
46-
is_template_base_of<Eigen::EigenBase, T>::value
47-
&& !is_eigen_dense<T>::value && !is_eigen_sparse<T>::value
45+
template <typename T> using is_eigen_base = all_of<
46+
is_template_base_of<Eigen::EigenBase, T>,
47+
negation<is_eigen_dense<T>>,
48+
negation<is_eigen_sparse<T>>
4849
>;
4950

5051
template<typename Type>

include/pybind11/pybind11.h

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ class cpp_function : public function {
132132
? &rec->data : rec->data[0]);
133133

134134
/* Override policy for rvalues -- always move */
135-
constexpr auto is_rvalue = !std::is_pointer<Return>::value
136-
&& !std::is_lvalue_reference<Return>::value;
135+
constexpr auto is_rvalue = detail::none_of<std::is_pointer<Return>,
136+
std::is_lvalue_reference<Return>>::value;
137137
const auto policy = is_rvalue ? return_value_policy::move : rec->policy;
138138

139139
/* Perform the function call */
@@ -922,14 +922,10 @@ NAMESPACE_END(detail)
922922
template <typename type_, typename... options>
923923
class class_ : public detail::generic_type {
924924
template <typename T> using is_holder = detail::is_holder_type<type_, T>;
925-
template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>;
926-
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-
>;
925+
template <typename T> using is_subtype = detail::all_of<std::is_base_of<type_, T>, detail::negation<std::is_same<T, type_>>>;
926+
template <typename T> using is_base = detail::all_of<std::is_base_of<T, type_>, detail::negation<std::is_same<T, type_>>>;
927+
// struct instead of using here to help MSVC:
928+
template <typename T> struct is_valid_class_option : detail::any_of<is_holder<T>, is_subtype<T>, is_base<T>> {};
933929

934930
public:
935931
using type = type_;
@@ -938,7 +934,7 @@ class class_ : public detail::generic_type {
938934
using holder_type = detail::first_of_t<is_holder, std::unique_ptr<type>, options...>;
939935
using instance_type = detail::instance<type, holder_type>;
940936

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

944940
PYBIND11_OBJECT(class_, generic_type, PyType_Check)

include/pybind11/pytypes.h

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,8 @@ class accessor : public object_api<accessor<Policy>> {
257257

258258
template <typename T = Policy>
259259
PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)")
260-
explicit operator enable_if_t<std::is_same<T, accessor_policies::str_attr>::value ||
261-
std::is_same<T, accessor_policies::obj_attr>::value, bool>() const {
260+
explicit operator enable_if_t<any_of<std::is_same<T, accessor_policies::str_attr>,
261+
std::is_same<T, accessor_policies::obj_attr>>::value, bool>() const {
262262
return hasattr(obj, key);
263263
}
264264
template <typename T = Policy>
@@ -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 = detail::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)