Enable detection of private operator new on MSVC

MSVC 2015 Update 3 and 2017 can handle enough expression SFINAE
to make this work now.
This commit is contained in:
Dean Moldovan 2017-06-08 18:21:12 +02:00
parent 6223b18cea
commit e27ea47c87
4 changed files with 24 additions and 21 deletions

View File

@ -492,23 +492,12 @@ public:
protected: protected:
typedef void *(*Constructor)(const void *stream); typedef void *(*Constructor)(const void *stream);
#if !defined(_MSC_VER)
/* Only enabled when the types are {copy,move}-constructible *and* when the type /* Only enabled when the types are {copy,move}-constructible *and* when the type
does not have a private operator new implementaton. */ does not have a private operator new implementaton. */
template <typename T = type, typename = enable_if_t<is_copy_constructible<T>::value>> static auto make_copy_constructor(const T *value) -> decltype(new T(*value), Constructor(nullptr)) { template <typename T = type, typename = enable_if_t<is_copy_constructible<T>::value>> 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)); }; } return [](const void *arg) -> void * { return new T(*((const T *) arg)); }; }
template <typename T = type> static auto make_move_constructor(const T *value) -> decltype(new T(std::move(*((T *) value))), Constructor(nullptr)) { template <typename T = type> 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(*const_cast<T *>(reinterpret_cast<const T *>(arg)))); }; } return [](const void *arg) -> void * { return (void *) new T(std::move(*const_cast<T *>(reinterpret_cast<const 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 = enable_if_t<is_copy_constructible<T>::value>>
static Constructor make_copy_constructor(const T *value) {
return [](const void *arg) -> void * { return new T(*((const T *)arg)); }; }
template <typename T = type, typename = enable_if_t<std::is_move_constructible<T>::value>>
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_copy_constructor(...) { return nullptr; }
static Constructor make_move_constructor(...) { return nullptr; } static Constructor make_move_constructor(...) { return nullptr; }

View File

@ -98,6 +98,12 @@ public:
}; };
}} }}
struct PrivateOpNew {
int value = 1;
private:
void *operator new(size_t bytes);
};
test_initializer copy_move_policies([](py::module &m) { test_initializer copy_move_policies([](py::module &m) {
py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor") py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
@ -174,4 +180,11 @@ test_initializer copy_move_policies([](py::module &m) {
m.attr("has_optional") = false; m.attr("has_optional") = false;
#endif #endif
// #70 compilation issue if operator new is not public
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
m.def("private_op_new_value", []() { return PrivateOpNew(); });
m.def("private_op_new_reference", []() -> const PrivateOpNew & {
static PrivateOpNew x{};
return x;
}, py::return_value_policy::reference);
}); });

View File

@ -98,3 +98,14 @@ def test_move_and_copy_load_optional():
assert c_c.copy_assignments == 2 assert c_c.copy_assignments == 2
assert c_c.copy_constructions == 5 assert c_c.copy_constructions == 5
assert c_m.alive() + c_mc.alive() + c_c.alive() == 0 assert c_m.alive() + c_mc.alive() + c_c.alive() == 0
def test_private_op_new():
"""An object with a private `operator new` cannot be returned by value"""
import pybind11_tests as m
with pytest.raises(RuntimeError) as excinfo:
m.private_op_new_value()
assert "the object is neither movable nor copyable" in str(excinfo.value)
assert m.private_op_new_reference().value == 1

View File

@ -77,16 +77,6 @@ template <> struct hash<TplConstrClass> { size_t operator()(const TplConstrClass
void init_issues(py::module &m) { void init_issues(py::module &m) {
py::module m2 = m.def_submodule("issues"); py::module m2 = m.def_submodule("issues");
#if !defined(_MSC_VER)
// Visual Studio 2015 currently cannot compile this test
// (see the comment in type_caster_base::make_copy_constructor)
// #70 compilation issue if operator new is not public
class NonConstructible { private: void *operator new(size_t bytes) throw(); };
py::class_<NonConstructible>(m, "Foo");
m2.def("getstmt", []() -> NonConstructible * { return nullptr; },
py::return_value_policy::reference);
#endif
// #137: const char* isn't handled properly // #137: const char* isn't handled properly
m2.def("print_cchar", [](const char *s) { return std::string(s); }); m2.def("print_cchar", [](const char *s) { return std::string(s); });