Skip to content

Relax and optimize all_of_t/any_of_t #554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -1004,23 +1004,24 @@ class type_caster<T, enable_if_t<is_pyobject<T>::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 <typename T, typename SFINAE = void> struct move_is_plain_type : std::false_type {};
template <typename T> struct move_is_plain_type<T, enable_if_t<
!std::is_void<T>::value && !std::is_pointer<T>::value && !std::is_reference<T>::value && !std::is_const<T>::value
>> : std::true_type { };
template <typename T> using move_is_plain_type = none_of<
std::is_void<T>, std::is_pointer<T>, std::is_reference<T>, std::is_const<T>
>;
template <typename T, typename SFINAE = void> struct move_always : std::false_type {};
template <typename T> struct move_always<T, enable_if_t<
move_is_plain_type<T>::value &&
!std::is_copy_constructible<T>::value && std::is_move_constructible<T>::value &&
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>::value
>> : std::true_type { };
template <typename T> struct move_always<T, enable_if_t<all_of<
move_is_plain_type<T>,
negation<std::is_copy_constructible<T>>,
std::is_move_constructible<T>,
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>
>::value>> : std::true_type {};
template <typename T, typename SFINAE = void> struct move_if_unreferenced : std::false_type {};
template <typename T> struct move_if_unreferenced<T, enable_if_t<
move_is_plain_type<T>::value &&
!move_always<T>::value && std::is_move_constructible<T>::value &&
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>::value
>> : std::true_type { };
template <typename T> using move_never = std::integral_constant<bool, !move_always<T>::value && !move_if_unreferenced<T>::value>;
template <typename T> struct move_if_unreferenced<T, enable_if_t<all_of<
move_is_plain_type<T>,
negation<move_always<T>>,
std::is_move_constructible<T>,
std::is_same<decltype(std::declval<type_caster<T>>().operator T&()), T&>
>::value>> : std::true_type {};
template <typename T> using move_never = none_of<move_always<T>, move_if_unreferenced<T>>;

// 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
Expand Down Expand Up @@ -1080,7 +1081,7 @@ template <typename T> T handle::cast() const { return pybind11::cast<T>(*this);
template <> inline void handle::cast() const { return; }

template <typename T>
detail::enable_if_t<detail::move_always<T>::value || detail::move_if_unreferenced<T>::value, T> move(object &&obj) {
detail::enable_if_t<!detail::move_never<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"
Expand Down Expand Up @@ -1427,14 +1428,14 @@ class unpacking_collector {

/// Collect only positional arguments for a Python function call
template <return_value_policy policy, typename... Args,
typename = enable_if_t<all_of_t<is_positional, Args...>::value>>
typename = enable_if_t<all_of<is_positional<Args>...>::value>>
simple_collector<policy> collect_arguments(Args &&...args) {
return simple_collector<policy>(std::forward<Args>(args)...);
}

/// Collect all arguments, including keywords and unpacking (only instantiated when needed)
template <return_value_policy policy, typename... Args,
typename = enable_if_t<!all_of_t<is_positional, Args...>::value>>
typename = enable_if_t<!all_of<is_positional<Args>...>::value>>
unpacking_collector<policy> collect_arguments(Args &&...args) {
// Following argument order rules for generalized unpacking according to PEP 448
static_assert(
Expand Down
61 changes: 36 additions & 25 deletions include/pybind11/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
#endif

/// Index sequences
#if defined(PYBIND11_CPP14) || defined(_MSC_VER)
using std::index_sequence;
using std::make_index_sequence;
#else
Expand All @@ -356,6 +365,32 @@ template<size_t ...S> struct make_index_sequence_impl <0, S...> { typedef index_
template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type;
#endif

#if defined(PYBIND11_CPP17) || defined(_MSC_VER)
using std::bool_constant;
using std::negation;
#else
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <class T> using negation = bool_constant<!T::value>;
#endif

/// Compile-time all/any/none of that check the ::value of all template types
#ifdef PYBIND11_CPP17
template <class... Ts> using all_of = bool_constant<(Ts::value && ...)>;
template <class... Ts> using any_of = bool_constant<(Ts::value || ...)>;
#elif !defined(_MSC_VER)
template <bool...> struct bools {};
template <class... Ts> using all_of = std::is_same<
bools<Ts::value..., true>,
bools<true, Ts::value...>>;
template <class... Ts> using any_of = negation<all_of<negation<Ts>...>>;
#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 <class... Ts> using all_of = std::conjunction<Ts...>;
template <class... Ts> using any_of = std::disjunction<Ts...>;
#endif
template <class... Ts> using none_of = negation<any_of<Ts...>>;

/// Strip the class from a method type
template <typename T> struct remove_class { };
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); };
Expand All @@ -377,35 +412,11 @@ struct void_type { };
/// Helper template which holds a list of types
template <typename...> struct type_list { };

/// from __cpp_future__ import (convenient aliases from C++14/17)
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <class T> using negation = bool_constant<!T::value>;
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;

/// Compile-time integer sum
constexpr size_t constexpr_sum() { return 0; }
template <typename T, typename... Ts>
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 <template<typename> class Predicate, typename... Ts>
using count_t = std::integral_constant<size_t, constexpr_sum(Predicate<Ts>::value...)>;
#else
// MSVC workaround (2015 Update 3 has issues with some member type aliases and constexpr)
template <template<typename> class Predicate, typename... Ts> struct count_t;
template <template<typename> class Predicate> struct count_t<Predicate> : std::integral_constant<size_t, 0> {};
template <template<typename> class Predicate, class T, class... Ts>
struct count_t<Predicate, T, Ts...> : std::integral_constant<size_t, Predicate<T>::value + count_t<Predicate, Ts...>::value> {};
#endif

/// Return true if all/any Ts satify Predicate<T>
template <template<typename> class Predicate, typename... Ts>
using all_of_t = bool_constant<(count_t<Predicate, Ts...>::value == sizeof...(Ts))>;
template <template<typename> class Predicate, typename... Ts>
using any_of_t = bool_constant<(count_t<Predicate, Ts...>::value > 0)>;

// Extracts the first type from the template parameter pack matching the predicate, or Default if none match.
template <template<class> class Predicate, class Default, class... Ts> struct first_of;
template <template<class> class Predicate, class Default> struct first_of<Predicate, Default> {
Expand Down
7 changes: 4 additions & 3 deletions include/pybind11/eigen.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ template <typename T> using is_eigen_ref = is_template_base_of<Eigen::RefBase, T
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
// SelfAdjointView fall into this category.
template <typename T> using is_eigen_base = bool_constant<
is_template_base_of<Eigen::EigenBase, T>::value
&& !is_eigen_dense<T>::value && !is_eigen_sparse<T>::value
template <typename T> using is_eigen_base = all_of<
is_template_base_of<Eigen::EigenBase, T>,
negation<is_eigen_dense<T>>,
negation<is_eigen_sparse<T>>
>;

template<typename Type>
Expand Down
11 changes: 4 additions & 7 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -924,12 +924,9 @@ class class_ : public detail::generic_type {
template <typename T> using is_holder = detail::is_holder_type<type_, T>;
template <typename T> using is_subtype = detail::bool_constant<std::is_base_of<type_, T>::value && !std::is_same<T, type_>::value>;
template <typename T> using is_base = detail::bool_constant<std::is_base_of<T, type_>::value && !std::is_same<T, type_>::value>;
template <typename T> using is_valid_class_option =
detail::bool_constant<
is_holder<T>::value ||
is_subtype<T>::value ||
is_base<T>::value
>;
// struct instead of using here to help MSVC:
template <typename T> struct is_valid_class_option :
detail::any_of<is_holder<T>, is_subtype<T>, is_base<T>> {};

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

static_assert(detail::all_of_t<is_valid_class_option, options...>::value,
static_assert(detail::all_of<is_valid_class_option<options>...>::value,
"Unknown/invalid class_ template parameters provided");

PYBIND11_OBJECT(class_, generic_type, PyType_Check)
Expand Down
10 changes: 4 additions & 6 deletions include/pybind11/pytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,10 @@ class args_proxy : public handle {
template <typename T> using is_keyword = std::is_base_of<arg, T>;
template <typename T> using is_s_unpacking = std::is_same<args_proxy, T>; // * unpacking
template <typename T> using is_ds_unpacking = std::is_same<kwargs_proxy, T>; // ** unpacking
template <typename T> using is_positional = bool_constant<
!is_keyword<T>::value && !is_s_unpacking<T>::value && !is_ds_unpacking<T>::value
>;
template <typename T> using is_keyword_or_ds = bool_constant<
is_keyword<T>::value || is_ds_unpacking<T>::value
template <typename T> using is_positional = none_of<
is_keyword<T>, is_s_unpacking<T>, is_ds_unpacking<T>
>;
template <typename T> using is_keyword_or_ds = any_of<is_keyword<T>, is_ds_unpacking<T>>;

// Call argument collector forward declarations
template <return_value_policy policy = return_value_policy::automatic_reference>
Expand Down Expand Up @@ -754,7 +752,7 @@ class dict : public object {
if (!m_ptr) pybind11_fail("Could not allocate dict object!");
}
template <typename... Args,
typename = detail::enable_if_t<detail::all_of_t<detail::is_keyword_or_ds, Args...>::value>,
typename = detail::enable_if_t<detail::all_of<detail::is_keyword_or_ds<Args>...>::value>,
// MSVC workaround: it can't compile an out-of-line definition, so defer the collector
typename collector = detail::deferred_t<detail::unpacking_collector<>, Args...>>
explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }
Expand Down
4 changes: 2 additions & 2 deletions include/pybind11/stl_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,8 @@ void vector_modifiers(enable_if_t<std::is_copy_constructible<typename Vector::va

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

// The usual case: access and iterate by reference
template <typename Vector, typename Class_>
Expand Down
5 changes: 5 additions & 0 deletions tests/test_stl_binders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
#include <deque>
#include <unordered_map>

#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;
Expand Down