Skip to content
Merged
69 changes: 66 additions & 3 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,64 @@ 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 <typename cast_in, typename cast_out>
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<N> 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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a comment to explain (or at least hint) why this works only with compilers other than MSVC?

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++17 also made static constexpr variables inline by default. Could you suppress the definitions in C++17 mode rather than adding the warnings? I think that might make this clearer

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea. I will try that now.

// provide an out-of-line definition of the class member. We should
// remove the following chunk of code (until the PYBIND11_WARNING_POP)
// if/when we drop support for C++11 and C++14.
PYBIND11_WARNING_PUSH
#if defined(PYBIND11_CPP17)
# if defined(__clang_major__) \
&& (__clang_major__ >= 17 || (defined(__APPLE__) && __clang_major__ >= 15))
// Even with the above gating, there's one straggler CI job that
// claims it doesn't know what
// -Wdeprecated-redundant-constexpr-static-def is despite being on
// Apple Clang 15. Just suppress -Wunknown-warning-option.
PYBIND11_WARNING_DISABLE_CLANG("-Wunknown-warning-option")
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated-redundant-constexpr-static-def")
# endif
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
#endif
template <typename cast_in, typename cast_out>
constexpr typename ReadableFunctionSignature<cast_in, cast_out>::sig_type
ReadableFunctionSignature<cast_in, cast_out>::kSig;
#if !defined(_MSC_VER)
template <typename cast_in, typename cast_out>
constexpr typename ReadableFunctionSignature<cast_in, cast_out>::types_type
ReadableFunctionSignature<cast_in, cast_out>::kTypes;
#endif
PYBIND11_WARNING_POP

PYBIND11_NAMESPACE_END(detail)

/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
Expand Down Expand Up @@ -481,9 +539,14 @@ class cpp_function : public function {

/* 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<cast_in, cast_out>::kSig;
#if !defined(_MSC_VER)
static constexpr const auto &types
= detail::ReadableFunctionSignature<cast_in, cast_out>::kTypes;
#else
PYBIND11_DESCR_CONSTEXPR auto types = std::decay<decltype(signature)>::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.
Expand Down
Loading