From 689867927033d3de22241b4e8e95d2bf25a350a7 Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Wed, 30 Aug 2017 23:40:55 +0200 Subject: [PATCH] Update enum_ and bind_vector to new-style init and pickle Fixes #1046. --- include/pybind11/pybind11.h | 54 ++++++++++++++++++------------------- include/pybind11/stl_bind.h | 30 ++++++++++----------- tests/test_buffers.cpp | 10 ++++--- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index b6e5fa699..fbde332a5 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1313,6 +1313,30 @@ private: } }; +/// Binds an existing constructor taking arguments Args... +template detail::initimpl::constructor init() { return {}; } +/// Like `init()`, but the instance is always constructed through the alias class (even +/// when not inheriting on the Python side). +template detail::initimpl::alias_constructor init_alias() { return {}; } + +/// Binds a factory function as a constructor +template > +Ret init(Func &&f) { return {std::forward(f)}; } + +/// Dual-argument factory function: the first function is called when no alias is needed, the second +/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. +template > +Ret init(CFunc &&c, AFunc &&a) { + return {std::forward(c), std::forward(a)}; +} + +/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type +/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. +template +detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { + return {std::forward(g), std::forward(s)}; +}; + /// Binds C++ enumerations and enumeration classes to Python template class enum_ : public class_ { public: @@ -1340,7 +1364,7 @@ public: m[kv.first] = kv.second; return m; }, return_value_policy::copy); - def("__init__", [](Type& value, Scalar i) { value = (Type)i; }); + def(init([](Scalar i) { return static_cast(i); })); def("__int__", [](Type value) { return (Scalar) value; }); #if PY_MAJOR_VERSION < 3 def("__long__", [](Type value) { return (Scalar) value; }); @@ -1378,8 +1402,8 @@ public: } def("__hash__", [](const Type &value) { return (Scalar) value; }); // Pickling and unpickling -- needed for use with the 'multiprocessing' module - def("__getstate__", [](const Type &value) { return pybind11::make_tuple((Scalar) value); }); - def("__setstate__", [](Type &p, tuple t) { new (&p) Type((Type) t[0].cast()); }); + def(pickle([](const Type &value) { return pybind11::make_tuple((Scalar) value); }, + [](tuple t) { return static_cast(t[0].cast()); })); } /// Export enumeration entries into the parent scope @@ -1402,30 +1426,6 @@ private: handle m_parent; }; -/// Binds an existing constructor taking arguments Args... -template detail::initimpl::constructor init() { return {}; } -/// Like `init()`, but the instance is always constructed through the alias class (even -/// when not inheriting on the Python side). -template detail::initimpl::alias_constructor init_alias() { return {}; } - -/// Binds a factory function as a constructor -template > -Ret init(Func &&f) { return {std::forward(f)}; } - -/// Dual-argument factory function: the first function is called when no alias is needed, the second -/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. -template > -Ret init(CFunc &&c, AFunc &&a) { - return {std::forward(c), std::forward(a)}; -} - -/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type -/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. -template -detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { - return {std::forward(g), std::forward(s)}; -}; - NAMESPACE_BEGIN(detail) diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index 31d84b2c9..7ef687878 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -120,17 +120,13 @@ void vector_modifiers(enable_if_t()); - } catch (...) { - v.~Vector(); - throw; - } - }); + cl.def(init([](iterable it) { + auto v = std::unique_ptr(new Vector()); + v->reserve(len(it)); + for (handle h : it) + v->push_back(h.cast()); + return v.release(); + })); cl.def("extend", [](Vector &v, const Vector &src) { @@ -346,20 +342,22 @@ vector_buffer(Class_& cl) { return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); }); - cl.def("__init__", [](Vector& vec, buffer buf) { + cl.def(init([](buffer buf) { auto info = buf.request(); if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) throw type_error("Only valid 1D buffers can be copied to a vector"); if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); - new (&vec) Vector(); - vec.reserve((size_t) info.shape[0]); + + auto vec = std::unique_ptr(new Vector()); + vec->reserve((size_t) info.shape[0]); T *p = static_cast(info.ptr); ssize_t step = info.strides[0] / static_cast(sizeof(T)); T *end = p + info.shape[0] * step; for (; p != end; p += step) - vec.push_back(*p); - }); + vec->push_back(*p); + return vec.release(); + })); return; } diff --git a/tests/test_buffers.cpp b/tests/test_buffers.cpp index c7f081d68..5be717730 100644 --- a/tests/test_buffers.cpp +++ b/tests/test_buffers.cpp @@ -78,13 +78,15 @@ TEST_SUBMODULE(buffers, m) { py::class_(m, "Matrix", py::buffer_protocol()) .def(py::init()) /// Construct from a buffer - .def("__init__", [](Matrix &v, py::buffer b) { + .def(py::init([](py::buffer b) { py::buffer_info info = b.request(); if (info.format != py::format_descriptor::format() || info.ndim != 2) throw std::runtime_error("Incompatible buffer format!"); - new (&v) Matrix(info.shape[0], info.shape[1]); - memcpy(v.data(), info.ptr, sizeof(float) * (size_t) (v.rows() * v.cols())); - }) + + auto v = new Matrix(info.shape[0], info.shape[1]); + memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols())); + return v; + })) .def("rows", &Matrix::rows) .def("cols", &Matrix::cols)