copy/move constructor detection workaround (MSVC 2015 bug)

This commit is contained in:
Wenzel Jakob 2016-05-01 10:28:00 +02:00
parent 8e93df825e
commit 2bc946bd7a
2 changed files with 23 additions and 23 deletions

View File

@ -225,6 +225,7 @@ using cast_op_type = typename std::conditional<std::is_pointer<typename std::rem
typename std::add_pointer<typename intrinsic_type<T>::type>::type, typename std::add_pointer<typename intrinsic_type<T>::type>::type,
typename std::add_lvalue_reference<typename intrinsic_type<T>::type>::type>::type; typename std::add_lvalue_reference<typename intrinsic_type<T>::type>::type>::type;
/// Generic type caster for objects stored on the heap /// Generic type caster for objects stored on the heap
template <typename type> class type_caster_base : public type_caster_generic { template <typename type> class type_caster_base : public type_caster_generic {
public: public:
@ -247,22 +248,35 @@ public:
static handle cast(const type *src, return_value_policy policy, handle parent) { static handle cast(const type *src, return_value_policy policy, handle parent) {
return type_caster_generic::cast( return type_caster_generic::cast(
src, policy, parent, src ? &typeid(*src) : nullptr, &typeid(type), src, policy, parent, src ? &typeid(*src) : nullptr, &typeid(type),
&copy_constructor, &move_constructor); make_copy_constructor(src), make_move_constructor(src));
} }
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>; template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
operator type*() { return (type *) value; } operator type*() { return (type *) value; }
operator type&() { return *((type *) value); } operator type&() { return *((type *) value); }
protected: protected:
template <typename T = type, typename std::enable_if<detail::is_copy_constructible<T>::value, int>::type = 0> typedef void *(*Constructor)(const void *stream);
static void *copy_constructor(const void *arg) { return (void *) new type(*((const type *) arg)); } #if !defined(_MSC_VER)
template <typename T = type, typename std::enable_if<!detail::is_copy_constructible<T>::value, int>::type = 0> /* Only enabled when the types are {copy,move}-constructible *and* when the type
static void *copy_constructor(const void *) { return nullptr; } does not have a private operator new implementaton. */
template <typename T = type, typename std::enable_if<detail::is_move_constructible<T>::value, int>::type = 0> template <typename T = type> static auto make_copy_constructor(const T *value) -> decltype(new T(*value), Constructor(nullptr)) {
static void *move_constructor(const void *arg) { return (void *) new type(std::move(*((type *) arg))); } return [](const void *arg) -> void * { return new T(*((const T *) arg)); }; }
template <typename T = type, typename std::enable_if<!detail::is_move_constructible<T>::value, int>::type = 0> template <typename T = type> static auto make_move_constructor(const T *value) -> decltype(new T(std::move(*((T *) value))), Constructor(nullptr)) {
static void *move_constructor(const void *) { return nullptr; } return [](const void *arg) -> void * { return (void *) new T(std::move(*((T *) arg))); }; }
#else
/* Visual Studio 2015's SFINAE implementation doesn't yet handle the above robustly in all situations.
Use a workaround that only tests for constructibility for now. */
template <typename T = type, typename = typename std::enable_if<std::is_copy_constructible<T>::value>::type>
static Constructor make_copy_constructor(const T *value) {
return [](const void *arg) -> void * { return new T(*((const T *)arg)); }; }
template <typename T = type, typename = typename std::enable_if<std::is_move_constructible<T>::value>::type>
static Constructor make_move_constructor(const T *value) {
return [](const void *arg) -> void * { return (void *) new T(std::move(*((T *)arg))); }; }
#endif
static Constructor make_copy_constructor(...) { return nullptr; }
static Constructor make_move_constructor(...) { return nullptr; }
}; };
template <typename type, typename SFINAE = void> class type_caster : public type_caster_base<type> { }; template <typename type, typename SFINAE = void> class type_caster : public type_caster_base<type> { };

View File

@ -293,20 +293,6 @@ template <typename T> struct intrinsic_type<T&&> { typedef type
template <typename T, size_t N> struct intrinsic_type<const T[N]> { typedef typename intrinsic_type<T>::type type; }; template <typename T, size_t N> struct intrinsic_type<const T[N]> { typedef typename intrinsic_type<T>::type type; };
template <typename T, size_t N> struct intrinsic_type<T[N]> { typedef typename intrinsic_type<T>::type type; }; template <typename T, size_t N> struct intrinsic_type<T[N]> { typedef typename intrinsic_type<T>::type type; };
/** \brief SFINAE helper class to check if a copy constructor is usable (in contrast to
* std::is_copy_constructible, this class also checks if the 'new' operator is accessible */
template <typename T> struct is_copy_constructible {
template <typename T2> static std::true_type test(decltype(new T2(std::declval<typename std::add_lvalue_reference<T2>::type>())) *);
template <typename T2> static std::false_type test(...);
static const bool value = std::is_same<std::true_type, decltype(test<T>(nullptr))>::value;
};
template <typename T> struct is_move_constructible {
template <typename T2> static std::true_type test(decltype(new T2(std::declval<T2>())) *);
template <typename T2> static std::false_type test(...);
static const bool value = std::is_same<std::true_type, decltype(test<T>(nullptr))>::value;
};
/// Helper type to replace 'void' in some expressions /// Helper type to replace 'void' in some expressions
struct void_type { }; struct void_type { };