Update enum_ and bind_vector to new-style init and pickle

Fixes #1046.
This commit is contained in:
Dean Moldovan 2017-08-30 23:40:55 +02:00
parent 4c5404421f
commit 6898679270
3 changed files with 47 additions and 47 deletions

View File

@ -1313,6 +1313,30 @@ private:
} }
}; };
/// Binds an existing constructor taking arguments Args...
template <typename... Args> detail::initimpl::constructor<Args...> init() { return {}; }
/// Like `init<Args...>()`, but the instance is always constructed through the alias class (even
/// when not inheriting on the Python side).
template <typename... Args> detail::initimpl::alias_constructor<Args...> init_alias() { return {}; }
/// Binds a factory function as a constructor
template <typename Func, typename Ret = detail::initimpl::factory<Func>>
Ret init(Func &&f) { return {std::forward<Func>(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 <typename CFunc, typename AFunc, typename Ret = detail::initimpl::factory<CFunc, AFunc>>
Ret init(CFunc &&c, AFunc &&a) {
return {std::forward<CFunc>(c), std::forward<AFunc>(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 <typename GetState, typename SetState>
detail::initimpl::pickle_factory<GetState, SetState> pickle(GetState &&g, SetState &&s) {
return {std::forward<GetState>(g), std::forward<SetState>(s)};
};
/// Binds C++ enumerations and enumeration classes to Python /// Binds C++ enumerations and enumeration classes to Python
template <typename Type> class enum_ : public class_<Type> { template <typename Type> class enum_ : public class_<Type> {
public: public:
@ -1340,7 +1364,7 @@ public:
m[kv.first] = kv.second; m[kv.first] = kv.second;
return m; return m;
}, return_value_policy::copy); }, return_value_policy::copy);
def("__init__", [](Type& value, Scalar i) { value = (Type)i; }); def(init([](Scalar i) { return static_cast<Type>(i); }));
def("__int__", [](Type value) { return (Scalar) value; }); def("__int__", [](Type value) { return (Scalar) value; });
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
def("__long__", [](Type value) { return (Scalar) value; }); def("__long__", [](Type value) { return (Scalar) value; });
@ -1378,8 +1402,8 @@ public:
} }
def("__hash__", [](const Type &value) { return (Scalar) value; }); def("__hash__", [](const Type &value) { return (Scalar) value; });
// Pickling and unpickling -- needed for use with the 'multiprocessing' module // Pickling and unpickling -- needed for use with the 'multiprocessing' module
def("__getstate__", [](const Type &value) { return pybind11::make_tuple((Scalar) value); }); def(pickle([](const Type &value) { return pybind11::make_tuple((Scalar) value); },
def("__setstate__", [](Type &p, tuple t) { new (&p) Type((Type) t[0].cast<Scalar>()); }); [](tuple t) { return static_cast<Type>(t[0].cast<Scalar>()); }));
} }
/// Export enumeration entries into the parent scope /// Export enumeration entries into the parent scope
@ -1402,30 +1426,6 @@ private:
handle m_parent; handle m_parent;
}; };
/// Binds an existing constructor taking arguments Args...
template <typename... Args> detail::initimpl::constructor<Args...> init() { return {}; }
/// Like `init<Args...>()`, but the instance is always constructed through the alias class (even
/// when not inheriting on the Python side).
template <typename... Args> detail::initimpl::alias_constructor<Args...> init_alias() { return {}; }
/// Binds a factory function as a constructor
template <typename Func, typename Ret = detail::initimpl::factory<Func>>
Ret init(Func &&f) { return {std::forward<Func>(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 <typename CFunc, typename AFunc, typename Ret = detail::initimpl::factory<CFunc, AFunc>>
Ret init(CFunc &&c, AFunc &&a) {
return {std::forward<CFunc>(c), std::forward<AFunc>(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 <typename GetState, typename SetState>
detail::initimpl::pickle_factory<GetState, SetState> pickle(GetState &&g, SetState &&s) {
return {std::forward<GetState>(g), std::forward<SetState>(s)};
};
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)

View File

@ -120,17 +120,13 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
arg("x"), arg("x"),
"Add an item to the end of the list"); "Add an item to the end of the list");
cl.def("__init__", [](Vector &v, iterable it) { cl.def(init([](iterable it) {
new (&v) Vector(); auto v = std::unique_ptr<Vector>(new Vector());
try { v->reserve(len(it));
v.reserve(len(it)); for (handle h : it)
for (handle h : it) v->push_back(h.cast<T>());
v.push_back(h.cast<T>()); return v.release();
} catch (...) { }));
v.~Vector();
throw;
}
});
cl.def("extend", cl.def("extend",
[](Vector &v, const Vector &src) { [](Vector &v, const Vector &src) {
@ -346,20 +342,22 @@ vector_buffer(Class_& cl) {
return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)}); return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)});
}); });
cl.def("__init__", [](Vector& vec, buffer buf) { cl.def(init([](buffer buf) {
auto info = buf.request(); auto info = buf.request();
if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T)))
throw type_error("Only valid 1D buffers can be copied to a vector"); throw type_error("Only valid 1D buffers can be copied to a vector");
if (!detail::compare_buffer_info<T>::compare(info) || (ssize_t) sizeof(T) != info.itemsize) if (!detail::compare_buffer_info<T>::compare(info) || (ssize_t) sizeof(T) != info.itemsize)
throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor<T>::format() + ")"); throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor<T>::format() + ")");
new (&vec) Vector();
vec.reserve((size_t) info.shape[0]); auto vec = std::unique_ptr<Vector>(new Vector());
vec->reserve((size_t) info.shape[0]);
T *p = static_cast<T*>(info.ptr); T *p = static_cast<T*>(info.ptr);
ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T)); ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
T *end = p + info.shape[0] * step; T *end = p + info.shape[0] * step;
for (; p != end; p += step) for (; p != end; p += step)
vec.push_back(*p); vec->push_back(*p);
}); return vec.release();
}));
return; return;
} }

View File

@ -78,13 +78,15 @@ TEST_SUBMODULE(buffers, m) {
py::class_<Matrix>(m, "Matrix", py::buffer_protocol()) py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
.def(py::init<ssize_t, ssize_t>()) .def(py::init<ssize_t, ssize_t>())
/// Construct from a buffer /// Construct from a buffer
.def("__init__", [](Matrix &v, py::buffer b) { .def(py::init([](py::buffer b) {
py::buffer_info info = b.request(); py::buffer_info info = b.request();
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
throw std::runtime_error("Incompatible buffer format!"); 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("rows", &Matrix::rows)
.def("cols", &Matrix::cols) .def("cols", &Matrix::cols)