Skip to content

Commit 6898679

Browse files
committed
Update enum_ and bind_vector to new-style init and pickle
Fixes #1046.
1 parent 4c54044 commit 6898679

File tree

3 files changed

+47
-47
lines changed

3 files changed

+47
-47
lines changed

include/pybind11/pybind11.h

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,30 @@ class class_ : public detail::generic_type {
13131313
}
13141314
};
13151315

1316+
/// Binds an existing constructor taking arguments Args...
1317+
template <typename... Args> detail::initimpl::constructor<Args...> init() { return {}; }
1318+
/// Like `init<Args...>()`, but the instance is always constructed through the alias class (even
1319+
/// when not inheriting on the Python side).
1320+
template <typename... Args> detail::initimpl::alias_constructor<Args...> init_alias() { return {}; }
1321+
1322+
/// Binds a factory function as a constructor
1323+
template <typename Func, typename Ret = detail::initimpl::factory<Func>>
1324+
Ret init(Func &&f) { return {std::forward<Func>(f)}; }
1325+
1326+
/// Dual-argument factory function: the first function is called when no alias is needed, the second
1327+
/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical.
1328+
template <typename CFunc, typename AFunc, typename Ret = detail::initimpl::factory<CFunc, AFunc>>
1329+
Ret init(CFunc &&c, AFunc &&a) {
1330+
return {std::forward<CFunc>(c), std::forward<AFunc>(a)};
1331+
}
1332+
1333+
/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type
1334+
/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`.
1335+
template <typename GetState, typename SetState>
1336+
detail::initimpl::pickle_factory<GetState, SetState> pickle(GetState &&g, SetState &&s) {
1337+
return {std::forward<GetState>(g), std::forward<SetState>(s)};
1338+
};
1339+
13161340
/// Binds C++ enumerations and enumeration classes to Python
13171341
template <typename Type> class enum_ : public class_<Type> {
13181342
public:
@@ -1340,7 +1364,7 @@ template <typename Type> class enum_ : public class_<Type> {
13401364
m[kv.first] = kv.second;
13411365
return m;
13421366
}, return_value_policy::copy);
1343-
def("__init__", [](Type& value, Scalar i) { value = (Type)i; });
1367+
def(init([](Scalar i) { return static_cast<Type>(i); }));
13441368
def("__int__", [](Type value) { return (Scalar) value; });
13451369
#if PY_MAJOR_VERSION < 3
13461370
def("__long__", [](Type value) { return (Scalar) value; });
@@ -1378,8 +1402,8 @@ template <typename Type> class enum_ : public class_<Type> {
13781402
}
13791403
def("__hash__", [](const Type &value) { return (Scalar) value; });
13801404
// Pickling and unpickling -- needed for use with the 'multiprocessing' module
1381-
def("__getstate__", [](const Type &value) { return pybind11::make_tuple((Scalar) value); });
1382-
def("__setstate__", [](Type &p, tuple t) { new (&p) Type((Type) t[0].cast<Scalar>()); });
1405+
def(pickle([](const Type &value) { return pybind11::make_tuple((Scalar) value); },
1406+
[](tuple t) { return static_cast<Type>(t[0].cast<Scalar>()); }));
13831407
}
13841408

13851409
/// Export enumeration entries into the parent scope
@@ -1402,30 +1426,6 @@ template <typename Type> class enum_ : public class_<Type> {
14021426
handle m_parent;
14031427
};
14041428

1405-
/// Binds an existing constructor taking arguments Args...
1406-
template <typename... Args> detail::initimpl::constructor<Args...> init() { return {}; }
1407-
/// Like `init<Args...>()`, but the instance is always constructed through the alias class (even
1408-
/// when not inheriting on the Python side).
1409-
template <typename... Args> detail::initimpl::alias_constructor<Args...> init_alias() { return {}; }
1410-
1411-
/// Binds a factory function as a constructor
1412-
template <typename Func, typename Ret = detail::initimpl::factory<Func>>
1413-
Ret init(Func &&f) { return {std::forward<Func>(f)}; }
1414-
1415-
/// Dual-argument factory function: the first function is called when no alias is needed, the second
1416-
/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical.
1417-
template <typename CFunc, typename AFunc, typename Ret = detail::initimpl::factory<CFunc, AFunc>>
1418-
Ret init(CFunc &&c, AFunc &&a) {
1419-
return {std::forward<CFunc>(c), std::forward<AFunc>(a)};
1420-
}
1421-
1422-
/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type
1423-
/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`.
1424-
template <typename GetState, typename SetState>
1425-
detail::initimpl::pickle_factory<GetState, SetState> pickle(GetState &&g, SetState &&s) {
1426-
return {std::forward<GetState>(g), std::forward<SetState>(s)};
1427-
};
1428-
14291429
NAMESPACE_BEGIN(detail)
14301430

14311431

include/pybind11/stl_bind.h

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,13 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
120120
arg("x"),
121121
"Add an item to the end of the list");
122122

123-
cl.def("__init__", [](Vector &v, iterable it) {
124-
new (&v) Vector();
125-
try {
126-
v.reserve(len(it));
127-
for (handle h : it)
128-
v.push_back(h.cast<T>());
129-
} catch (...) {
130-
v.~Vector();
131-
throw;
132-
}
133-
});
123+
cl.def(init([](iterable it) {
124+
auto v = std::unique_ptr<Vector>(new Vector());
125+
v->reserve(len(it));
126+
for (handle h : it)
127+
v->push_back(h.cast<T>());
128+
return v.release();
129+
}));
134130

135131
cl.def("extend",
136132
[](Vector &v, const Vector &src) {
@@ -346,20 +342,22 @@ vector_buffer(Class_& cl) {
346342
return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)});
347343
});
348344

349-
cl.def("__init__", [](Vector& vec, buffer buf) {
345+
cl.def(init([](buffer buf) {
350346
auto info = buf.request();
351347
if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T)))
352348
throw type_error("Only valid 1D buffers can be copied to a vector");
353349
if (!detail::compare_buffer_info<T>::compare(info) || (ssize_t) sizeof(T) != info.itemsize)
354350
throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor<T>::format() + ")");
355-
new (&vec) Vector();
356-
vec.reserve((size_t) info.shape[0]);
351+
352+
auto vec = std::unique_ptr<Vector>(new Vector());
353+
vec->reserve((size_t) info.shape[0]);
357354
T *p = static_cast<T*>(info.ptr);
358355
ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
359356
T *end = p + info.shape[0] * step;
360357
for (; p != end; p += step)
361-
vec.push_back(*p);
362-
});
358+
vec->push_back(*p);
359+
return vec.release();
360+
}));
363361

364362
return;
365363
}

tests/test_buffers.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,15 @@ TEST_SUBMODULE(buffers, m) {
7878
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
7979
.def(py::init<ssize_t, ssize_t>())
8080
/// Construct from a buffer
81-
.def("__init__", [](Matrix &v, py::buffer b) {
81+
.def(py::init([](py::buffer b) {
8282
py::buffer_info info = b.request();
8383
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
8484
throw std::runtime_error("Incompatible buffer format!");
85-
new (&v) Matrix(info.shape[0], info.shape[1]);
86-
memcpy(v.data(), info.ptr, sizeof(float) * (size_t) (v.rows() * v.cols()));
87-
})
85+
86+
auto v = new Matrix(info.shape[0], info.shape[1]);
87+
memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols()));
88+
return v;
89+
}))
8890

8991
.def("rows", &Matrix::rows)
9092
.def("cols", &Matrix::cols)

0 commit comments

Comments
 (0)