@@ -89,6 +89,7 @@ class autoclass_impl {
8989 py::owned_ref<PyTypeObject> m_type;
9090 PyType_Spec m_spec;
9191 py::owned_ref<PyTypeObject> m_py_basetype;
92+ py::owned_ref<> m_module;
9293
9394 /* * Check if this type uses the `Py_TPFLAGS_HAVE_GC`, which requires that we implement
9495 at least `Py_tp_traverse`, and will use `PyObject_GC_New` and `PyObject_GC_Del`.
@@ -153,32 +154,32 @@ class autoclass_impl {
153154
154155 // dispatch for free function that accepts as a first argument `T`
155156 template <typename R, typename ... Args, auto impl>
156- struct free_function_impl <R(*)(T, Args...), impl>
157+ struct free_function_impl <R (*)(T, Args...), impl>
157158 : public free_function_base<impl, R, Args...> {};
158159
159160 // dispatch for free function that accepts as a first argument `T&`
160161 template <typename R, typename ... Args, auto impl>
161- struct free_function_impl <R(*)(T&, Args...), impl>
162+ struct free_function_impl <R (*)(T&, Args...), impl>
162163 : public free_function_base<impl, R, Args...> {};
163164
164165 // dispatch for free function that accepts as a first argument `const T&`
165166 template <typename R, typename ... Args, auto impl>
166- struct free_function_impl <R(*)(const T&, Args...), impl>
167+ struct free_function_impl <R (*)(const T&, Args...), impl>
167168 : public free_function_base<impl, R, Args...> {};
168169
169170 // dispatch for a noexcept free function that accepts as a first argument `T`
170171 template <typename R, typename ... Args, auto impl>
171- struct free_function_impl <R(*)(T, Args...) noexcept , impl>
172+ struct free_function_impl <R (*)(T, Args...) noexcept , impl>
172173 : public free_function_base<impl, R, Args...> {};
173174
174175 // dispatch for noexcept free function that accepts as a first argument `T&`
175176 template <typename R, typename ... Args, auto impl>
176- struct free_function_impl <R(*)(T&, Args...) noexcept , impl>
177+ struct free_function_impl <R (*)(T&, Args...) noexcept , impl>
177178 : public free_function_base<impl, R, Args...> {};
178179
179180 // dispatch for a noexcept free function that accepts as a first argument `const T&`
180181 template <typename R, typename ... Args, auto impl>
181- struct free_function_impl <R(*)(const T&, Args...) noexcept , impl>
182+ struct free_function_impl <R (*)(const T&, Args...) noexcept , impl>
182183 : public free_function_base<impl, R, Args...> {};
183184
184185 template <auto impl, typename R, typename ... Args>
@@ -336,19 +337,42 @@ class autoclass_impl {
336337 return static_cast <void *>(std::addressof (unbox (ob)));
337338 }
338339
340+ private:
341+ std::string name_in_module (py::borrowed_ref<> module , std::string_view name) {
342+ if (!module ) {
343+ return std::string{name};
344+ }
345+
346+ const char * const module_name = PyModule_GetName (module .get ());
347+ if (!module_name) {
348+ throw py::exception{};
349+ }
350+ return py::util::format_string (module_name, " ." , name);
351+ }
352+
339353public:
340- autoclass_impl (std::string name = util::type_name<T>(),
354+ /* * Construct the type and add it to a module.
355+
356+ @param module The module to add the type to.
357+ @param name The name of the type as seen from Python.
358+ @param extra_flags Extra flags to forward to `tp_flags` field.
359+ @param base_type A Python type to subclass.
360+ */
361+ autoclass_impl (py::borrowed_ref<> module ,
362+ std::string name = py::util::type_name<T>(),
341363 int extra_flags = 0 ,
342364 py::borrowed_ref<PyTypeObject> base_type = nullptr )
343- : m_storage(std::make_unique<detail::autoclass_storage>(dynamic_unbox,
344- std::move (name))),
365+ : m_storage(
366+ std::make_unique<detail::autoclass_storage>(dynamic_unbox,
367+ name_in_module (module , name))),
345368 m_type(nullptr ),
346369 m_spec({m_storage->strings .front ().data (),
347370 static_cast <int >(sizeof (object)),
348371 0 ,
349372 flags (extra_flags, base_type),
350373 nullptr }),
351- m_py_basetype(py::owned_ref<PyTypeObject>::xnew_reference(base_type)) {
374+ m_py_basetype(py::owned_ref<PyTypeObject>::xnew_reference(base_type)),
375+ m_module(py::owned_ref<>::xnew_reference(module )) {
352376 if (base_type) {
353377 // Check to make sure that the static base type is not obviously
354378 // wrong. This check does not ensure that the static base type is
@@ -402,18 +426,23 @@ class autoclass_impl {
402426 add_slot (Py_tp_dealloc, py_dealloc);
403427 }
404428
405- // Delete the copy constructor, the intermediate string data points into
406- // storage that is managed by the type until `.type()` is called.
407- // Also, don't try to create 2 versions of the same type.
429+ autoclass_impl (std::string name = util::type_name<T>(),
430+ int extra_flags = 0,
431+ py::borrowed_ref<PyTypeObject> base_type = nullptr)
432+ : autoclass_impl(nullptr , std::move(name), extra_flags, base_type) {}
433+
434+ // Delete the copy constructor, the intermediate string data points into storage
435+ // that is managed by the type until `.type()` is called. Also, don't try to
436+ // create 2 versions of the same type.
408437 autoclass_impl (const autoclass_impl&) = delete;
409438 autoclass_impl (autoclass_impl&&) = default;
410439 autoclass_impl& operator =(autoclass_impl&&) = default ;
411440
412441 /* * Add a `tp_traverse` field to this type. This is only allowed, but required if
413442 `extra_flags & Py_TPFLAGS_HAVE_GC`.
414443
415- @tparam impl The implementation of the traverse function. This should either be an
416- `int(T&, visitproc, void*)` or `int (T::*)(visitproc, void*)`.
444+ @tparam impl The implementation of the traverse function. This should either
445+ be an `int(T&, visitproc, void*)` or `int (T::*)(visitproc, void*)`.
417446 */
418447 template <auto impl>
419448 concrete& traverse () {
@@ -441,8 +470,8 @@ class autoclass_impl {
441470 /* * Add a `tp_clear` field to this type. This is only allowed if
442471 `extra_flags & Py_TPFLAGS_HAVE_GC`.
443472
444- @tparam impl The implementation of the clear function. This should either be an
445- `int(T&)` or `int (T::*)()`.
473+ @tparam impl The implementation of the clear function. This should either be
474+ an `int(T&)` or `int (T::*)()`.
446475 */
447476 template <auto impl>
448477 concrete& clear () {
@@ -1157,13 +1186,12 @@ class autoclass_impl {
11571186 iter_name += " ::iterator" ;
11581187
11591188 // create the iterator class and put it in the cache
1160- if (!autoclass<iter>(std::move (iter_name), Py_TPFLAGS_HAVE_GC)
1161- .add_slot (Py_tp_iternext, static_cast <iternextfunc>(iternext))
1162- .add_slot (Py_tp_iter, &PyObject_SelfIter)
1163- .template traverse <&iter::traverse>()
1164- .type ()) {
1165- throw py::exception{};
1166- }
1189+ autoclass<iter>(std::move (iter_name), Py_TPFLAGS_HAVE_GC)
1190+ .add_slot (Py_tp_iternext, static_cast <iternextfunc>(iternext))
1191+ .add_slot (Py_tp_iter, &PyObject_SelfIter)
1192+ .template traverse <&iter::traverse>()
1193+ .type ()
1194+ .escape ();
11671195
11681196 return [](PyObject* self) -> PyObject* {
11691197 try {
@@ -1438,6 +1466,16 @@ class autoclass_impl {
14381466 release_type_cache.dismiss ();
14391467
14401468 m_type = type;
1469+
1470+ if (m_module) {
1471+ const char * const last_dot = std::strrchr (m_type.get ()->tp_name , ' .' );
1472+ if (!last_dot) {
1473+ throw py::exception (PyExc_RuntimeError, " no '.' in type name" );
1474+ }
1475+ PyObject_SetAttrString (m_module.get (),
1476+ last_dot + 1 ,
1477+ static_cast <PyObject*>(type));
1478+ }
14411479 return type;
14421480 }
14431481
0 commit comments