diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 686bf68d5..ffb489357 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -225,6 +225,7 @@ using cast_op_type = typename std::conditional::type>::type, typename std::add_lvalue_reference::type>::type>::type; + /// Generic type caster for objects stored on the heap template class type_caster_base : public type_caster_generic { public: @@ -247,22 +248,35 @@ public: static handle cast(const type *src, return_value_policy policy, handle parent) { return type_caster_generic::cast( src, policy, parent, src ? &typeid(*src) : nullptr, &typeid(type), - ©_constructor, &move_constructor); + make_copy_constructor(src), make_move_constructor(src)); } template using cast_op_type = pybind11::detail::cast_op_type; operator type*() { return (type *) value; } operator type&() { return *((type *) value); } + protected: - template ::value, int>::type = 0> - static void *copy_constructor(const void *arg) { return (void *) new type(*((const type *) arg)); } - template ::value, int>::type = 0> - static void *copy_constructor(const void *) { return nullptr; } - template ::value, int>::type = 0> - static void *move_constructor(const void *arg) { return (void *) new type(std::move(*((type *) arg))); } - template ::value, int>::type = 0> - static void *move_constructor(const void *) { return nullptr; } + typedef void *(*Constructor)(const void *stream); +#if !defined(_MSC_VER) + /* Only enabled when the types are {copy,move}-constructible *and* when the type + does not have a private operator new implementaton. */ + template static auto make_copy_constructor(const T *value) -> decltype(new T(*value), Constructor(nullptr)) { + return [](const void *arg) -> void * { return new T(*((const T *) arg)); }; } + template static auto make_move_constructor(const T *value) -> decltype(new T(std::move(*((T *) value))), Constructor(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 ::value>::type> + static Constructor make_copy_constructor(const T *value) { + return [](const void *arg) -> void * { return new T(*((const T *)arg)); }; } + template ::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 class type_caster : public type_caster_base { }; diff --git a/include/pybind11/common.h b/include/pybind11/common.h index 91de74987..a82bb0ebe 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -293,20 +293,6 @@ template struct intrinsic_type { typedef type template struct intrinsic_type { typedef typename intrinsic_type::type type; }; template struct intrinsic_type { typedef typename intrinsic_type::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 struct is_copy_constructible { - template static std::true_type test(decltype(new T2(std::declval::type>())) *); - template static std::false_type test(...); - static const bool value = std::is_same(nullptr))>::value; -}; - -template struct is_move_constructible { - template static std::true_type test(decltype(new T2(std::declval())) *); - template static std::false_type test(...); - static const bool value = std::is_same(nullptr))>::value; -}; - /// Helper type to replace 'void' in some expressions struct void_type { };