Skip to content

Commit 2b96543

Browse files
committed
Enable static properties by default
Now that only one shared metaclass is ever allocated, it's extremely cheap to enable it for all pybind11 types.
1 parent 6845aae commit 2b96543

File tree

7 files changed

+25
-54
lines changed

7 files changed

+25
-54
lines changed

docs/advanced/classes.rst

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -437,24 +437,15 @@ The section on :ref:`properties` discussed the creation of instance properties
437437
that are implemented in terms of C++ getters and setters.
438438

439439
Static properties can also be created in a similar way to expose getters and
440-
setters of static class attributes. Two things are important to note:
441-
442-
1. Static properties are implemented by instrumenting the *metaclass* of the
443-
class in question -- however, this requires the class to have a modifiable
444-
metaclass in the first place. pybind11 provides a ``py::metaclass()``
445-
annotation that must be specified in the ``class_`` constructor, or any
446-
later method calls to ``def_{property_,∅}_{readwrite,readonly}_static`` will
447-
fail (see the example below).
448-
449-
2. For static properties defined in terms of setter and getter functions, note
450-
that the implicit ``self`` argument also exists in this case and is used to
451-
pass the Python ``type`` subclass instance. This parameter will often not be
452-
needed by the C++ side, and the following example illustrates how to
453-
instantiate a lambda getter function that ignores it:
440+
setters of static class attributes. Note that the implicit ``self`` argument
441+
also exists in this case and is used to pass the Python ``type`` subclass
442+
instance. This parameter will often not be needed by the C++ side, and the
443+
following example illustrates how to instantiate a lambda getter function
444+
that ignores it:
454445

455446
.. code-block:: cpp
456447
457-
py::class_<Foo>(m, "Foo", py::metaclass())
448+
py::class_<Foo>(m, "Foo")
458449
.def_property_readonly_static("foo", [](py::object /* self */) { return Foo(); });
459450
460451
Operator overloading

include/pybind11/attr.h

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ struct dynamic_attr { };
5353
/// Annotation which enables the buffer protocol for a type
5454
struct buffer_protocol { };
5555

56-
/// Annotation which requests that a special metaclass is created for a type
57-
struct metaclass { };
56+
/// DEPRECATED: Annotation which requests that a special metaclass is created for a type
57+
struct metaclass {
58+
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
59+
metaclass() = default;
60+
};
5861

5962
/// Annotation to mark enums as an arithmetic type
6063
struct arithmetic { };
@@ -149,8 +152,7 @@ struct function_record {
149152
/// Special data structure which (temporarily) holds metadata about a bound class
150153
struct type_record {
151154
PYBIND11_NOINLINE type_record()
152-
: multiple_inheritance(false), dynamic_attr(false),
153-
buffer_protocol(false), metaclass(false) { }
155+
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false) { }
154156

155157
/// Handle to the parent scope
156158
handle scope;
@@ -188,9 +190,6 @@ struct type_record {
188190
/// Does the class implement the buffer protocol?
189191
bool buffer_protocol : 1;
190192

191-
/// Does the class require its own metaclass?
192-
bool metaclass : 1;
193-
194193
/// Is the default (unique_ptr) holder type used?
195194
bool default_holder : 1;
196195

@@ -354,11 +353,8 @@ struct process_attribute<buffer_protocol> : process_attribute_default<buffer_pro
354353
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
355354
};
356355

357-
template <>
358-
struct process_attribute<metaclass> : process_attribute_default<metaclass> {
359-
static void init(const metaclass &, type_record *r) { r->metaclass = true; }
360-
};
361-
356+
// DEPRECATED
357+
template <> struct process_attribute<metaclass> : process_attribute_default<metaclass> { };
362358

363359
/// Process an 'arithmetic' attribute for enums (does nothing here)
364360
template <>

include/pybind11/class_support.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ inline PyObject *make_object_base_type(size_t instance_size) {
276276
issue no Python C API calls which could potentially invoke the
277277
garbage collector (the GC will call type_traverse(), which will in
278278
turn find the newly constructed type in an invalid state) */
279-
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
279+
auto metaclass = get_internals().default_metaclass;
280+
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
280281
if (!heap_type)
281282
pybind11_fail("make_object_base_type(): error allocating type!");
282283

@@ -365,7 +366,8 @@ inline PyObject* make_new_python_type(const type_record &rec) {
365366
issue no Python C API calls which could potentially invoke the
366367
garbage collector (the GC will call type_traverse(), which will in
367368
turn find the newly constructed type in an invalid state) */
368-
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
369+
auto metaclass = internals.default_metaclass;
370+
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
369371
if (!heap_type)
370372
pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
371373

@@ -382,12 +384,6 @@ inline PyObject* make_new_python_type(const type_record &rec) {
382384
if (bases.size() > 0)
383385
type->tp_bases = bases.release().ptr();
384386

385-
/* Custom metaclass if requested (used for static properties) */
386-
if (rec.metaclass) {
387-
Py_INCREF(internals.default_metaclass);
388-
Py_TYPE(type) = (PyTypeObject *) internals.default_metaclass;
389-
}
390-
391387
/* Supported protocols */
392388
type->tp_as_number = &heap_type->as_number;
393389
type->tp_as_sequence = &heap_type->as_sequence;

include/pybind11/pybind11.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -829,18 +829,6 @@ class generic_type : public object {
829829
const auto is_static = !(rec_fget->is_method && rec_fget->scope);
830830
const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings();
831831

832-
if (is_static) {
833-
auto mclass = handle((PyObject *) Py_TYPE(m_ptr));
834-
835-
if ((PyTypeObject *) mclass.ptr() == &PyType_Type)
836-
pybind11_fail(
837-
"Adding static properties to the type '" +
838-
std::string(((PyTypeObject *) m_ptr)->tp_name) +
839-
"' requires the type to have a custom metaclass. Please "
840-
"ensure that one is created by supplying the pybind11::metaclass() "
841-
"annotation to the associated class_<>(..) invocation.");
842-
}
843-
844832
auto property = handle((PyObject *) (is_static ? get_internals().static_property_type
845833
: &PyProperty_Type));
846834
attr(name) = property(fget.ptr() ? fget : none(),

tests/test_methods_and_attributes.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ test_initializer methods_and_attributes([](py::module &m) {
202202
.def("__str__", &ExampleMandA::toString)
203203
.def_readwrite("value", &ExampleMandA::value);
204204

205-
py::class_<TestProperties>(m, "TestProperties", py::metaclass())
205+
py::class_<TestProperties>(m, "TestProperties")
206206
.def(py::init<>())
207207
.def_readonly("def_readonly", &TestProperties::value)
208208
.def_readwrite("def_readwrite", &TestProperties::value)
@@ -228,7 +228,7 @@ test_initializer methods_and_attributes([](py::module &m) {
228228
auto static_set2 = [](py::object, int v) { TestPropRVP::sv2.value = v; };
229229
auto rvp_copy = py::return_value_policy::copy;
230230

231-
py::class_<TestPropRVP>(m, "TestPropRVP", py::metaclass())
231+
py::class_<TestPropRVP>(m, "TestPropRVP")
232232
.def(py::init<>())
233233
.def_property_readonly("ro_ref", &TestPropRVP::get1)
234234
.def_property_readonly("ro_copy", &TestPropRVP::get2, rvp_copy)

tests/test_multiple_inheritance.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,24 +123,24 @@ test_initializer mi_static_properties([](py::module &pm) {
123123
.def(py::init<>())
124124
.def("vanilla", &Vanilla::vanilla);
125125

126-
py::class_<WithStatic1>(m, "WithStatic1", py::metaclass())
126+
py::class_<WithStatic1>(m, "WithStatic1")
127127
.def(py::init<>())
128128
.def_static("static_func1", &WithStatic1::static_func1)
129129
.def_readwrite_static("static_value1", &WithStatic1::static_value1);
130130

131-
py::class_<WithStatic2>(m, "WithStatic2", py::metaclass())
131+
py::class_<WithStatic2>(m, "WithStatic2")
132132
.def(py::init<>())
133133
.def_static("static_func2", &WithStatic2::static_func2)
134134
.def_readwrite_static("static_value2", &WithStatic2::static_value2);
135135

136136
py::class_<VanillaStaticMix1, Vanilla, WithStatic1, WithStatic2>(
137-
m, "VanillaStaticMix1", py::metaclass())
137+
m, "VanillaStaticMix1")
138138
.def(py::init<>())
139139
.def_static("static_func", &VanillaStaticMix1::static_func)
140140
.def_readwrite_static("static_value", &VanillaStaticMix1::static_value);
141141

142142
py::class_<VanillaStaticMix2, WithStatic1, Vanilla, WithStatic2>(
143-
m, "VanillaStaticMix2", py::metaclass())
143+
m, "VanillaStaticMix2")
144144
.def(py::init<>())
145145
.def_static("static_func", &VanillaStaticMix2::static_func)
146146
.def_readwrite_static("static_value", &VanillaStaticMix2::static_value);

tests/test_python_types.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ struct MoveOutContainer {
190190
test_initializer python_types([](py::module &m) {
191191
/* No constructor is explicitly defined below. An exception is raised when
192192
trying to construct it directly from Python */
193-
py::class_<ExamplePythonTypes>(m, "ExamplePythonTypes", "Example 2 documentation", py::metaclass())
193+
py::class_<ExamplePythonTypes>(m, "ExamplePythonTypes", "Example 2 documentation")
194194
.def("get_dict", &ExamplePythonTypes::get_dict, "Return a Python dictionary")
195195
.def("get_dict_2", &ExamplePythonTypes::get_dict_2, "Return a C++ dictionary")
196196
.def("get_list", &ExamplePythonTypes::get_list, "Return a Python list")

0 commit comments

Comments
 (0)