Show a deprecation warning for old-style `__init__` and `__setstate__`
The warning is shown at module initialization time (on import, not
when the functions are called). It's only visible when compiled in
debug mode.
diff --git a/docs/upgrade.rst b/docs/upgrade.rst
index bcbc6b1..f993e20 100644
--- a/docs/upgrade.rst
+++ b/docs/upgrade.rst
@@ -37,6 +37,72 @@
}
+New API for defining custom constructors and pickling functions
+---------------------------------------------------------------
+
+The old placement-new custom constructors have been deprecated. The new approach
+uses ``py::init()`` and factory functions to greatly improve type safety.
+
+Placement-new can be called accidentally with an incompatible type (without any
+compiler errors or warnings), or it can initialize the same object multiple times
+if not careful with the Python-side ``__init__`` calls. The new-style custom
+constructors prevent such mistakes. See :ref:`custom_constructors` for details.
+
+.. code-block:: cpp
+
+ // old -- deprecated (runtime warning shown only in debug mode)
+ py::class<Foo>(m, "Foo")
+ .def("__init__", [](Foo &self, ...) {
+ new (&self) Foo(...); // uses placement-new
+ });
+
+ // new
+ py::class<Foo>(m, "Foo")
+ .def(py::init([](...) { // Note: no `self` argument
+ return new Foo(...); // return by raw pointer
+ // or: return std::make_unique<Foo>(...); // return by holder
+ // or: return Foo(...); // return by value (move constructor)
+ }));
+
+Mirroring the custom constructor changes, ``py::pickle()`` is now the preferred
+way to get and set object state. See :ref:`pickling` for details.
+
+.. code-block:: cpp
+
+ // old -- deprecated (runtime warning shown only in debug mode)
+ py::class<Foo>(m, "Foo")
+ ...
+ .def("__getstate__", [](const Foo &self) {
+ return py::make_tuple(self.value1(), self.value2(), ...);
+ })
+ .def("__setstate__", [](Foo &self, py::tuple t) {
+ new (&self) Foo(t[0].cast<std::string>(), ...);
+ });
+
+ // new
+ py::class<Foo>(m, "Foo")
+ ...
+ .def(py::pickle(
+ [](const Foo &self) { // __getstate__
+ return py::make_tuple(f.value1(), f.value2(), ...); // unchanged
+ },
+ [](py::tuple t) { // __setstate__, note: no `self` argument
+ return new Foo(t[0].cast<std::string>(), ...);
+ // or: return std::make_unique<Foo>(...); // return by holder
+ // or: return Foo(...); // return by value (move constructor)
+ }
+ ));
+
+For both the constructors and pickling, warnings are shown at module
+initialization time (on import, not when the functions are called).
+They're only visible when compiled in debug mode. Sample warning:
+
+.. code-block:: none
+
+ pybind11-bound class 'mymodule.Foo' is using an old-style placement-new '__init__'
+ which has been deprecated. See the upgrade guide in pybind11's docs.
+
+
Stricter enforcement of hidden symbol visibility for pybind11 modules
---------------------------------------------------------------------
@@ -135,66 +201,6 @@
third-party modules.
-New syntax for custom constructors
-----------------------------------
-
-The old placement-new custom constructors are still valid, but the new approach
-greatly improves type safety. Placement-new can be called accidentally with an
-incompatible type (without any compiler errors or warnings), or it can initialize
-the same object multiple times if not careful with the Python-side ``__init__``
-calls. The new-style ``py::init()`` custom constructors prevent such mistakes.
-See :ref:`custom_constructors` for details.
-
-.. code-block:: cpp
-
- // old
- py::class<Foo>(m, "Foo")
- .def("__init__", [](Foo &self, ...) {
- new (&self) Foo(...); // uses placement-new
- });
-
- // new
- py::class<Foo>(m, "Foo")
- .def(py::init([](...) { // Note: no `self` argument
- return new Foo(...); // return by raw pointer
- // or: return std::make_unique<Foo>(...); // return by holder
- // or: return Foo(...); // return by value (move constructor)
- }));
-
-
-New syntax for pickling support
--------------------------------
-
-Mirroring the custom constructor changes, ``py::pickle()`` is now the preferred
-way to get and set object state. See :ref:`pickling` for details.
-
-.. code-block:: cpp
-
- // old -- deprecated
- py::class<Foo>(m, "Foo")
- ...
- .def("__getstate__", [](const Foo &self) {
- return py::make_tuple(self.value1(), self.value2(), ...);
- })
- .def("__setstate__", [](Foo &self, py::tuple t) {
- new (&self) Foo(t[0].cast<std::string>(), ...);
- });
-
- // new
- py::class<Foo>(m, "Foo")
- ...
- .def(py::pickle(
- [](const Foo &self) { // __getstate__
- return py::make_tuple(f.value1(), f.value2(), ...); // unchanged
- },
- [](py::tuple t) { // __setstate__, note: no `self` argument
- return new Foo(t[0].cast<std::string>(), ...);
- // or: return std::make_unique<Foo>(...); // return by holder
- // or: return Foo(...); // return by value (move constructor)
- }
- ));
-
-
Deprecation of some ``py::object`` APIs
---------------------------------------
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index 0e67c40..b6e5fa6 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -201,6 +201,20 @@
rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__");
+#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
+ if (rec->is_constructor && !rec->is_new_style_constructor) {
+ const auto class_name = std::string(((PyTypeObject *) rec->scope.ptr())->tp_name);
+ const auto func_name = std::string(rec->name);
+ PyErr_WarnEx(
+ PyExc_FutureWarning,
+ ("pybind11-bound class '" + class_name + "' is using an old-style "
+ "placement-new '" + func_name + "' which has been deprecated. See "
+ "the upgrade guide in pybind11's docs. This message is only visible "
+ "when compiled in debug mode.").c_str(), 0
+ );
+ }
+#endif
+
/* Generate a proper function signature */
std::string signature;
size_t type_depth = 0, char_index = 0, type_index = 0, arg_index = 0;