@@ -112,7 +112,6 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
112112 size_t &arg_index) {
113113 std::string signature;
114114 bool is_starred = false ;
115- bool is_annotation = func_rec == nullptr ;
116115 // `is_return_value.top()` is true if we are currently inside the return type of the
117116 // signature. Using `@^`/`@$` we can force types to be arg/return types while `@!` pops
118117 // back to the previous state.
@@ -199,9 +198,7 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
199198 // For named arguments (py::arg()) with noconvert set, return value type is used.
200199 ++pc;
201200 if (!is_return_value.top ()
202- && (is_annotation
203- || !(arg_index < func_rec->args .size ()
204- && !func_rec->args [arg_index].convert ))) {
201+ && (!(arg_index < func_rec->args .size () && !func_rec->args [arg_index].convert ))) {
205202 while (*pc != ' \0 ' && *pc != ' @' ) {
206203 signature += *pc++;
207204 }
@@ -232,6 +229,19 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
232229 return signature;
233230}
234231
232+ template <typename T>
233+ inline std::string generate_type_signature () {
234+ static constexpr auto caster_name_field = make_caster<T>::name;
235+ PYBIND11_DESCR_CONSTEXPR auto descr_types = decltype (caster_name_field)::types ();
236+ // Create a default function_record to ensure the function signature has the proper
237+ // configuration e.g. no_convert.
238+ auto func_rec = function_record ();
239+ size_t type_index = 0 ;
240+ size_t arg_index = 0 ;
241+ return generate_function_signature (
242+ caster_name_field.text , &func_rec, descr_types.data (), type_index, arg_index);
243+ }
244+
235245#if defined(_MSC_VER)
236246# define PYBIND11_COMPAT_STRDUP _strdup
237247#else
@@ -1255,6 +1265,22 @@ class mod_gil_not_used {
12551265 bool flag_;
12561266};
12571267
1268+ PYBIND11_NAMESPACE_BEGIN (detail)
1269+
1270+ inline bool gil_not_used_option () { return false ; }
1271+ template <typename F, typename ... O>
1272+ bool gil_not_used_option (F &&, O &&...o );
1273+ template <typename ... O>
1274+ inline bool gil_not_used_option (mod_gil_not_used f, O &&...o ) {
1275+ return f.flag () || gil_not_used_option (o...);
1276+ }
1277+ template <typename F, typename ... O>
1278+ inline bool gil_not_used_option (F &&, O &&...o ) {
1279+ return gil_not_used_option (o...);
1280+ }
1281+
1282+ PYBIND11_NAMESPACE_END (detail)
1283+
12581284// / Wrapper for Python extension modules
12591285class module_ : public object {
12601286public:
@@ -1362,16 +1388,15 @@ class module_ : public object {
13621388 = mod_gil_not_used(false )) {
13631389 // module_def is PyModuleDef
13641390 // Placement new (not an allocation).
1365- def = new (def)
1366- PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1367- /* m_name */ name,
1368- /* m_doc */ options::show_user_defined_docstrings () ? doc : nullptr ,
1369- /* m_size */ -1 ,
1370- /* m_methods */ nullptr ,
1371- /* m_slots */ nullptr ,
1372- /* m_traverse */ nullptr ,
1373- /* m_clear */ nullptr ,
1374- /* m_free */ nullptr };
1391+ new (def) PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1392+ /* m_name */ name,
1393+ /* m_doc */ options::show_user_defined_docstrings () ? doc : nullptr ,
1394+ /* m_size */ -1 ,
1395+ /* m_methods */ nullptr ,
1396+ /* m_slots */ nullptr ,
1397+ /* m_traverse */ nullptr ,
1398+ /* m_clear */ nullptr ,
1399+ /* m_free */ nullptr };
13751400 auto *m = PyModule_Create (def);
13761401 if (m == nullptr ) {
13771402 if (PyErr_Occurred ()) {
@@ -1389,6 +1414,68 @@ class module_ : public object {
13891414 // For Python 2, reinterpret_borrow was correct.
13901415 return reinterpret_borrow<module_>(m);
13911416 }
1417+
1418+ // / Must be a POD type, and must hold enough entries for all of the possible slots PLUS ONE for
1419+ // / the sentinel (0) end slot.
1420+ using slots_array = std::array<PyModuleDef_Slot, 3 >;
1421+
1422+ /* * \rst
1423+ Initialized a module def for use with multi-phase module initialization.
1424+
1425+ ``def`` should point to a statically allocated module_def.
1426+ ``slots`` must already contain a Py_mod_exec or Py_mod_create slot and will be filled with
1427+ additional slots from the supplied options (and the empty sentinel slot).
1428+ \endrst */
1429+ template <typename ... Options>
1430+ static object initialize_multiphase_module_def (const char *name,
1431+ const char *doc,
1432+ module_def *def,
1433+ slots_array &slots,
1434+ Options &&...options) {
1435+ size_t next_slot = 0 ;
1436+ size_t term_slot = slots.size () - 1 ;
1437+
1438+ // find the end of the supplied slots
1439+ while (next_slot < term_slot && slots[next_slot].slot != 0 ) {
1440+ ++next_slot;
1441+ }
1442+
1443+ bool nogil PYBIND11_MAYBE_UNUSED = detail::gil_not_used_option (options...);
1444+ if (nogil) {
1445+ #if defined(Py_mod_gil) && defined(Py_GIL_DISABLED)
1446+ if (next_slot >= term_slot) {
1447+ pybind11_fail (" initialize_multiphase_module_def: not enough space in slots" );
1448+ }
1449+ slots[next_slot++] = {Py_mod_gil, Py_MOD_GIL_NOT_USED};
1450+ #endif
1451+ }
1452+
1453+ // slots must have a zero end sentinel
1454+ if (next_slot > term_slot) {
1455+ pybind11_fail (" initialize_multiphase_module_def: not enough space in slots" );
1456+ }
1457+ slots[next_slot++] = {0 , nullptr };
1458+
1459+ // module_def is PyModuleDef
1460+ // Placement new (not an allocation).
1461+ new (def) PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1462+ /* m_name */ name,
1463+ /* m_doc */ options::show_user_defined_docstrings () ? doc : nullptr ,
1464+ /* m_size */ 0 ,
1465+ /* m_methods */ nullptr ,
1466+ /* m_slots */ &slots[0 ],
1467+ /* m_traverse */ nullptr ,
1468+ /* m_clear */ nullptr ,
1469+ /* m_free */ nullptr };
1470+ auto *m = PyModuleDef_Init (def);
1471+ if (m == nullptr ) {
1472+ if (PyErr_Occurred ()) {
1473+ throw error_already_set ();
1474+ }
1475+ pybind11_fail (" Internal error in module_::initialize_multiphase_module_def()" );
1476+ }
1477+ return reinterpret_borrow<object>(m);
1478+ }
13921479};
13931480
13941481PYBIND11_NAMESPACE_BEGIN (detail)
0 commit comments