Skip to content

Commit d92fd4a

Browse files
committed
Add and use C++17-style conjunction/disjunction
C++17 has std::conjunction/disjunction which are very similar to our `all_of_t` and `any_of_t` in practice. (They can differ if you pass in a Predicate with a non-bool ::value, but we don't do that). Essentially `detail::all_of_t<P, T...>` is equivalent to `std::conjunction<P<T>...>`, and `any_of_t<P, T...>` corresponds to `std::disjunction<P<T>...>`. Unlike `any`/`all_of_t`, `dis`/`conjunction` aren't tied to a single predicate, so it can be used as a compile-time `and` across multiple types with `::value`s, allowing some nice reduction in code in a few places. I also removed the `count_t` since `any_of_t` and `all_of_t` was the only thing using it. This commit also uses aliases to the `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. 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.)
1 parent 3f1ff3f commit d92fd4a

File tree

7 files changed

+62
-66
lines changed

7 files changed

+62
-66
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: 21 additions & 20 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 = negation<disjunction<
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<conjunction<
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<conjunction<
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 = negation<disjunction<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 = conjunction<
1031+
disjunction<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
@@ -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<conjunction<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<!conjunction<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: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -345,17 +345,39 @@ 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;
350352
using std::index_sequence;
351353
using std::make_index_sequence;
352354
#else
355+
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
356+
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
353357
template<size_t ...> struct index_sequence { };
354358
template<size_t N, size_t ...S> struct make_index_sequence_impl : make_index_sequence_impl <N - 1, N - 1, S...> { };
355359
template<size_t ...S> struct make_index_sequence_impl <0, S...> { typedef index_sequence<S...> type; };
356360
template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type;
357361
#endif
358362

363+
#if defined(PYBIND11_CPP17) || defined(_MSC_VER)
364+
using std::bool_constant;
365+
using std::conjunction;
366+
using std::disjunction;
367+
using std::negation;
368+
#else
369+
template <bool B> using bool_constant = std::integral_constant<bool, B>;
370+
template <typename...> struct conjunction : std::true_type {};
371+
template <typename B> struct conjunction<B> : B {};
372+
template <typename B, typename... Bmore> struct conjunction<B, Bmore...>
373+
: conditional_t<B::value, conjunction<Bmore...>, B> {};
374+
template <typename...> struct disjunction : std::false_type {};
375+
template <typename B> struct disjunction<B> : B {};
376+
template <typename B, typename... Bmore> struct disjunction<B, Bmore...>
377+
: conditional_t<B::value, B, disjunction<Bmore...>> {};
378+
template <class T> using negation = bool_constant<!T::value>;
379+
#endif
380+
359381
/// Strip the class from a method type
360382
template <typename T> struct remove_class { };
361383
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); };
@@ -377,35 +399,11 @@ struct void_type { };
377399
/// Helper template which holds a list of types
378400
template <typename...> struct type_list { };
379401

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-
386402
/// Compile-time integer sum
387403
constexpr size_t constexpr_sum() { return 0; }
388404
template <typename T, typename... Ts>
389405
constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); }
390406

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-
409407
// Extracts the first type from the template parameter pack matching the predicate, or Default if none match.
410408
template <template<class> class Predicate, class Default, class... Ts> struct first_of;
411409
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 = conjunction<
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: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -924,12 +924,7 @@ 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+
template <typename T> struct is_valid_class_option : detail::disjunction<is_holder<T>, is_subtype<T>, is_base<T>> {};
933928

934929
public:
935930
using type = type_;
@@ -938,7 +933,7 @@ class class_ : public detail::generic_type {
938933
using holder_type = detail::first_of_t<is_holder, std::unique_ptr<type>, options...>;
939934
using instance_type = detail::instance<type, holder_type>;
940935

941-
static_assert(detail::all_of_t<is_valid_class_option, options...>::value,
936+
static_assert(detail::conjunction<is_valid_class_option<options>...>::value,
942937
"Unknown/invalid class_ template parameters provided");
943938

944939
PYBIND11_OBJECT(class_, generic_type, PyType_Check)

include/pybind11/pytypes.h

Lines changed: 7 additions & 9 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<disjunction<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
418-
>;
413+
template <typename T> using is_positional = negation<disjunction<
414+
is_keyword<T>, is_s_unpacking<T>, is_ds_unpacking<T>
415+
>>;
416+
template <typename T> using is_keyword_or_ds = disjunction<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::conjunction<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_>

0 commit comments

Comments
 (0)