Skip to content
Merged
54 changes: 51 additions & 3 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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
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
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
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.
#if !defined(PYBIND11_CPP17)
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
#endif

PYBIND11_NAMESPACE_END(detail)

/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
Expand Down Expand Up @@ -481,9 +524,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