From 1e5bc66e3835749d0ec942acfd261d5139692b20 Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Wed, 15 Oct 2025 21:12:44 -0700 Subject: [PATCH] Factor out readable function signatures to avoid duplication (#5857) * Centralize readable function signatures to avoid duplication This seems to reduce size costs of adding enum_-specific implementations of dunder methods, but also should provide a nice to have size optimization for programs that use pybind11 in general. * gate disabling of -Wdeprecated-redundant-constexpr-static-def to clang 17+ * fix gating to include Apple Clang 15 * Make GCC happy with types * fix apple clang gating again. suppress -Wdeprecated for GCC * Gate warning suppressions to C++17. Suppress -Wdeprecated for clang as well. * hopefully fix last straggler CI job * attempt to address readability review feedback from @rwgk * drop warning suppressions and instead just gate compilation the pre-C++17 compat code --- include/pybind11/pybind11.h | 54 ++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index fcf10f199..8ab4681c7 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -248,6 +248,49 @@ inline std::string generate_type_signature() { # define PYBIND11_COMPAT_STRDUP strdup #endif +#define PYBIND11_READABLE_FUNCTION_SIGNATURE_EXPR \ + detail::const_name("(") + cast_in::arg_names + detail::const_name(") -> ") + cast_out::name + +// We factor out readable function signatures to a specific template +// so that they don't get duplicated across different instantiations of +// cpp_function::initialize (which is templated on more types). +template +class ReadableFunctionSignature { +public: + using sig_type = decltype(PYBIND11_READABLE_FUNCTION_SIGNATURE_EXPR); + +private: + // We have to repeat PYBIND11_READABLE_FUNCTION_SIGNATURE_EXPR in decltype() + // because C++11 doesn't allow functions to return `auto`. (We don't + // know the type because it's some variant of detail::descr with + // unknown N.) + static constexpr sig_type sig() { return PYBIND11_READABLE_FUNCTION_SIGNATURE_EXPR; } + +public: + static constexpr sig_type kSig = sig(); + // We can only stash the result of detail::descr::types() in a + // constexpr variable if we aren't on MSVC (see + // PYBIND11_DESCR_CONSTEXPR). +#if !defined(_MSC_VER) + using types_type = decltype(sig_type::types()); + static constexpr types_type kTypes = sig_type::types(); +#endif +}; +#undef PYBIND11_READABLE_FUNCTION_SIGNATURE_EXPR + +// Prior to C++17, we don't have inline variables, so we have to +// provide an out-of-line definition of the class member. +#if !defined(PYBIND11_CPP17) +template +constexpr typename ReadableFunctionSignature::sig_type + ReadableFunctionSignature::kSig; +# if !defined(_MSC_VER) +template +constexpr typename ReadableFunctionSignature::types_type + ReadableFunctionSignature::kTypes; +# endif +#endif + PYBIND11_NAMESPACE_END(detail) /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object @@ -481,9 +524,14 @@ protected: /* Generate a readable signature describing the function's arguments and return value types */ - static constexpr auto signature - = const_name("(") + cast_in::arg_names + const_name(") -> ") + cast_out::name; - PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); + static constexpr const auto &signature + = detail::ReadableFunctionSignature::kSig; +#if !defined(_MSC_VER) + static constexpr const auto &types + = detail::ReadableFunctionSignature::kTypes; +#else + PYBIND11_DESCR_CONSTEXPR auto types = std::decay::type::types(); +#endif /* Register the function with Python from generic (non-templated) code */ // Pass on the ownership over the `unique_rec` to `initialize_generic`. `rec` stays valid.