From c10ac6cf1f69ebe2b7dd26957325ea3b161cb7ff Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Sun, 2 Jul 2017 11:48:56 +0200 Subject: [PATCH] Make it possible to generate constexpr signatures in C++11 mode The current C++14 constexpr signatures don't require relaxed constexpr, but only `auto` return type deduction. To get around this in C++11, the type caster's `name()` static member functions are turned into `static constexpr auto` variables. --- include/pybind11/cast.h | 30 +++++----- include/pybind11/detail/descr.h | 99 +++++---------------------------- include/pybind11/detail/init.h | 2 +- include/pybind11/eigen.h | 49 ++++++++-------- include/pybind11/functional.h | 6 +- include/pybind11/numpy.h | 74 ++++++++++++------------ include/pybind11/pybind11.h | 7 +-- include/pybind11/stl.h | 12 ++-- tests/test_copy_move.cpp | 2 +- 9 files changed, 103 insertions(+), 178 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index eab904bee..8c9493a64 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -778,7 +778,7 @@ template struct is_copy_constructible class type_caster_base : public type_caster_generic { using itype = intrinsic_t; public: - static PYBIND11_DESCR name() { return type_descr(_()); } + static constexpr auto name = type_descr(_()); type_caster_base() : type_caster_base(typeid(type)) { } explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } @@ -885,7 +885,7 @@ private: "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); public: bool load(handle src, bool convert) { return subcaster.load(src, convert); } - static PYBIND11_DESCR name() { return caster_t::name(); } + static constexpr auto name = caster_t::name; static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { // It is definitely wrong to take ownership of this pointer, so mask that rvp if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) @@ -900,7 +900,7 @@ public: protected: \ type value; \ public: \ - static PYBIND11_DESCR name() { return type_descr(py_name); } \ + static constexpr auto name = type_descr(py_name); \ template >::value, int> = 0> \ static handle cast(T_ *src, return_value_policy policy, handle parent) { \ if (!src) return none().release(); \ @@ -1049,7 +1049,7 @@ public: template using cast_op_type = void*&; operator void *&() { return value; } - static PYBIND11_DESCR name() { return type_descr(_("capsule")); } + static constexpr auto name = type_descr(_("capsule")); private: void *value = nullptr; }; @@ -1289,7 +1289,7 @@ public: return value[0]; } - static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); } + static constexpr auto name = type_descr(_(PYBIND11_STRING_NAME)); template using cast_op_type = remove_reference_t>; }; @@ -1314,9 +1314,9 @@ public: return cast_impl(std::forward(src), policy, parent, indices{}); } - static PYBIND11_DESCR name() { - return type_descr(_("Tuple[") + detail::concat(make_caster::name()...) + _("]")); - } + static constexpr auto name = type_descr( + _("Tuple[") + detail::concat(make_caster::name...) + _("]") + ); template using cast_op_type = type; @@ -1461,7 +1461,7 @@ struct move_only_holder_caster { auto *ptr = holder_helper::get(src); return type_caster_base::cast_holder(ptr, &src); } - static PYBIND11_DESCR name() { return type_caster_base::name(); } + static constexpr auto name = type_caster_base::name; }; template @@ -1492,10 +1492,10 @@ template struct is_holder_type : template struct is_holder_type> : std::true_type {}; -template struct handle_type_name { static PYBIND11_DESCR name() { return _(); } }; -template <> struct handle_type_name { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } }; -template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("*args"); } }; -template <> struct handle_type_name { static PYBIND11_DESCR name() { return _("**kwargs"); } }; +template struct handle_type_name { static constexpr auto name = _(); }; +template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; +template <> struct handle_type_name { static constexpr auto name = _("*args"); }; +template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; template struct pyobject_caster { @@ -1513,7 +1513,7 @@ struct pyobject_caster { static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { return src.inc_ref(); } - PYBIND11_TYPE_CASTER(type, handle_type_name::name()); + PYBIND11_TYPE_CASTER(type, handle_type_name::name); }; template @@ -1826,7 +1826,7 @@ public: static constexpr bool has_kwargs = kwargs_pos < 0; static constexpr bool has_args = args_pos < 0; - static PYBIND11_DESCR arg_names() { return detail::concat(make_caster::name()...); } + static constexpr auto arg_names = detail::concat(make_caster::name...); bool load_args(function_call &call) { return load_impl_sequence(call, indices{}); diff --git a/include/pybind11/detail/descr.h b/include/pybind11/detail/descr.h index e3bf2ba97..51e85d805 100644 --- a/include/pybind11/detail/descr.h +++ b/include/pybind11/detail/descr.h @@ -1,6 +1,5 @@ /* - pybind11/detail/descr.h: Helper type for concatenating type signatures - either at runtime (C++11) or compile time (C++14) + pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time Copyright (c) 2016 Wenzel Jakob @@ -15,13 +14,12 @@ NAMESPACE_BEGIN(PYBIND11_NAMESPACE) NAMESPACE_BEGIN(detail) -/* Concatenate type signatures at compile time using C++14 */ -#if defined(PYBIND11_CPP14) && !defined(_MSC_VER) -#define PYBIND11_CONSTEXPR_DESCR - +/* Concatenate type signatures at compile time */ template class descr { template friend class descr; public: + constexpr descr() = default; + constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) : descr(text, types, make_index_sequence(), @@ -96,90 +94,21 @@ template constexpr descr<1, 1> _() { return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); } -inline constexpr descr<0, 0> concat() { return _(""); } -template auto constexpr concat(descr descr) { return descr; } -template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } -template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } +constexpr descr<0, 0> concat() { return _(""); } -#define PYBIND11_DESCR constexpr auto +template +constexpr descr concat(descr descr) { return descr; } -#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ - -class descr { -public: - PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { - size_t nChars = len(text), nTypes = len(types); - m_text = new char[nChars]; - m_types = new const std::type_info *[nTypes]; - memcpy(m_text, text, nChars * sizeof(char)); - memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); - } - - PYBIND11_NOINLINE descr operator+(descr &&d2) && { - descr r; - - size_t nChars1 = len(m_text), nTypes1 = len(m_types); - size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); - - r.m_text = new char[nChars1 + nChars2 - 1]; - r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; - memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char)); - memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); - memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *)); - memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); - - delete[] m_text; delete[] m_types; - delete[] d2.m_text; delete[] d2.m_types; - - return r; - } - - char *text() { return m_text; } - const std::type_info * * types() { return m_types; } - -protected: - PYBIND11_NOINLINE descr() { } - - template static size_t len(const T *ptr) { // return length including null termination - const T *it = ptr; - while (*it++ != (T) 0) - ; - return static_cast(it - ptr); - } - - const std::type_info **m_types = nullptr; - char *m_text = nullptr; -}; - -/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ - -PYBIND11_NOINLINE inline descr _(const char *text) { - const std::type_info *types[1] = { nullptr }; - return descr(text, types); +template +constexpr auto concat(descr d, Args... args) + -> decltype(descr{} + concat(args...)) { + return d + _(", ") + concat(args...); } -template PYBIND11_NOINLINE enable_if_t _(const char *text1, const char *) { return _(text1); } -template PYBIND11_NOINLINE enable_if_t _(char const *, const char *text2) { return _(text2); } -template PYBIND11_NOINLINE enable_if_t _(descr d, descr) { return d; } -template PYBIND11_NOINLINE enable_if_t _(descr, descr d) { return d; } - -template PYBIND11_NOINLINE descr _() { - const std::type_info *types[2] = { &typeid(Type), nullptr }; - return descr("%", types); +template +constexpr descr type_descr(descr descr) { + return _("{") + descr + _("}"); } -template PYBIND11_NOINLINE descr _() { - const std::type_info *types[1] = { nullptr }; - return descr(std::to_string(Size).c_str(), types); -} - -PYBIND11_NOINLINE inline descr concat() { return _(""); } -PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } -template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } -PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } - -#define PYBIND11_DESCR ::pybind11::detail::descr -#endif - NAMESPACE_END(detail) NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index c3594a190..ded321144 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -24,7 +24,7 @@ public: template using cast_op_type = value_and_holder &; operator value_and_holder &() { return *value; } - static PYBIND11_DESCR name() { return type_descr(_()); } + static constexpr auto name = type_descr(_()); private: value_and_holder *value = nullptr; diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index a702bf39e..9d9a23ea7 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -180,28 +180,27 @@ template struct EigenProps { } } - static PYBIND11_DESCR descriptor() { - constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; - constexpr bool show_order = is_eigen_dense_map::value; - constexpr bool show_c_contiguous = show_order && requires_row_major; - constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; + static constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; + static constexpr bool show_order = is_eigen_dense_map::value; + static constexpr bool show_c_contiguous = show_order && requires_row_major; + static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; - return type_descr(_("numpy.ndarray[") + npy_format_descriptor::name() + - _("[") + _(_<(size_t) rows>(), _("m")) + - _(", ") + _(_<(size_t) cols>(), _("n")) + - _("]") + - // For a reference type (e.g. Ref) we have other constraints that might need to be - // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride - // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output - // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to - // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you - // *gave* a numpy.ndarray of the right type and dimensions. - _(", flags.writeable", "") + - _(", flags.c_contiguous", "") + - _(", flags.f_contiguous", "") + - _("]") - ); - } + static constexpr auto descriptor = type_descr( + _("numpy.ndarray[") + npy_format_descriptor::name + + _("[") + _(_<(size_t) rows>(), _("m")) + + _(", ") + _(_<(size_t) cols>(), _("n")) + + _("]") + + // For a reference type (e.g. Ref) we have other constraints that might need to be + // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride + // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output + // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to + // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you + // *gave* a numpy.ndarray of the right type and dimensions. + _(", flags.writeable", "") + + _(", flags.c_contiguous", "") + + _(", flags.f_contiguous", "") + + _("]") + ); }; // Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, @@ -337,7 +336,7 @@ public: return cast_impl(src, policy, parent); } - static PYBIND11_DESCR name() { return props::descriptor(); } + static constexpr auto name = props::descriptor; operator Type*() { return &value; } operator Type&() { return value; } @@ -385,7 +384,7 @@ public: } } - static PYBIND11_DESCR name() { return props::descriptor(); } + static constexpr auto name = props::descriptor; // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return // types but not bound arguments). We still provide them (with an explicitly delete) so that @@ -530,7 +529,7 @@ public: } static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } - static PYBIND11_DESCR name() { return props::descriptor(); } + static constexpr auto name = props::descriptor; // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return // types but not bound arguments). We still provide them (with an explicitly delete) so that @@ -597,7 +596,7 @@ struct type_caster::value>> { } PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") - + npy_format_descriptor::name() + _("]")); + + npy_format_descriptor::name + _("]")); }; NAMESPACE_END(detail) diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index eda14ba58..8c88c353b 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -75,10 +75,8 @@ public: return cpp_function(std::forward(f_), policy).release(); } - PYBIND11_TYPE_CASTER(type, _("Callable[[") + - argument_loader::arg_names() + _("], ") + - make_caster::name() + - _("]")); + PYBIND11_TYPE_CASTER(type, _("Callable[[") + argument_loader::arg_names + _("], ") + + make_caster::name + _("]")); }; NAMESPACE_END(detail) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 55bb81698..593a796ff 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -250,7 +250,7 @@ template struct array_info_scalar { typedef T type; static constexpr bool is_array = false; static constexpr bool is_empty = false; - static PYBIND11_DESCR extents() { return _(""); } + static constexpr auto extents = _(""); static void append_extents(list& /* shape */) { } }; // Computes underlying type and a comma-separated list of extents for array @@ -269,15 +269,9 @@ template struct array_info> { array_info::append_extents(shape); } - template::is_array, int> = 0> - static PYBIND11_DESCR extents() { - return _(); - } - - template::is_array, int> = 0> - static PYBIND11_DESCR extents() { - return concat(_(), array_info::extents()); - } + static constexpr auto extents = _::is_array>( + concat(_(), array_info::extents), _() + ); }; // For numpy we have special handling for arrays of characters, so we don't include // the size in the array extents. @@ -947,7 +941,7 @@ template struct format_descriptor::is_array>> { static std::string format() { using detail::_; - PYBIND11_DESCR extents = _("(") + detail::array_info::extents() + _(")"); + constexpr auto extents = _("(") + detail::array_info::extents + _(")"); return extents.text() + format_descriptor>::format(); } }; @@ -967,7 +961,7 @@ struct pyobject_caster> { static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { return src.inc_ref(); } - PYBIND11_TYPE_CASTER(type, handle_type_name::name()); + PYBIND11_TYPE_CASTER(type, handle_type_name::name); }; template @@ -977,7 +971,34 @@ struct compare_buffer_info::valu } }; -template struct npy_format_descriptor::value>> { +template +struct npy_format_descriptor_name; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value>( + _("bool"), _::value>("int", "uint") + _() + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value || std::is_same::value>( + _("float") + _(), _("longdouble") + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value + || std::is_same::value>( + _("complex") + _(), _("longcomplex") + ); +}; + +template +struct npy_format_descriptor::value>> + : npy_format_descriptor_name { private: // NB: the order here must match the one in common.h constexpr static const int values[15] = { @@ -996,25 +1017,10 @@ public: return reinterpret_borrow(ptr); pybind11_fail("Unsupported buffer format!"); } - template ::value, int> = 0> - static PYBIND11_DESCR name() { - return _::value>(_("bool"), - _::value>("int", "uint") + _()); - } - template ::value, int> = 0> - static PYBIND11_DESCR name() { - return _::value || std::is_same::value>( - _("float") + _(), _("longdouble")); - } - template ::value, int> = 0> - static PYBIND11_DESCR name() { - return _::value || std::is_same::value>( - _("complex") + _(), _("longcomplex")); - } }; #define PYBIND11_DECL_CHAR_FMT \ - static PYBIND11_DESCR name() { return _("S") + _(); } \ + static constexpr auto name = _("S") + _(); \ static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; @@ -1026,7 +1032,7 @@ private: public: static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); - static PYBIND11_DESCR name() { return _("(") + array_info::extents() + _(")") + base_descr::name(); } + static constexpr auto name = _("(") + array_info::extents + _(")") + base_descr::name; static pybind11::dtype dtype() { list shape; array_info::append_extents(shape); @@ -1038,7 +1044,7 @@ template struct npy_format_descriptor private: using base_descr = npy_format_descriptor::type>; public: - static PYBIND11_DESCR name() { return base_descr::name(); } + static constexpr auto name = base_descr::name; static pybind11::dtype dtype() { return base_descr::dtype(); } }; @@ -1113,7 +1119,7 @@ inline PYBIND11_NOINLINE void register_structured_dtype( template struct npy_format_descriptor { static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); - static PYBIND11_DESCR name() { return make_caster::name(); } + static constexpr auto name = make_caster::name; static pybind11::dtype dtype() { return reinterpret_borrow(dtype_ptr()); @@ -1558,9 +1564,7 @@ vectorize_extractor(const Func &f, Return (*) (Args ...)) { } template struct handle_type_name> { - static PYBIND11_DESCR name() { - return _("numpy.ndarray[") + npy_format_descriptor::name() + _("]"); - } + static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); }; NAMESPACE_END(detail) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 613135a7a..9ac4636d6 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -164,7 +164,7 @@ protected: /* Generate a readable signature describing the function's arguments and return value types */ using detail::descr; using detail::_; - PYBIND11_DESCR signature = _("(") + cast_in::arg_names() + _(") -> ") + cast_out::name(); + constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; /* Register the function with Python from generic (non-templated) code */ initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args)); @@ -275,11 +275,6 @@ protected: if (type_depth != 0 || types[type_index] != nullptr) pybind11_fail("Internal error while parsing type signature (2)"); - #if !defined(PYBIND11_CONSTEXPR_DESCR) - delete[] types; - delete[] text; - #endif - #if PY_MAJOR_VERSION < 3 if (strcmp(rec->name, "__next__") == 0) { std::free(rec->name); diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index db900e674..e690e43fa 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -91,7 +91,7 @@ template struct set_caster { return s.release(); } - PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name() + _("]")); + PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]")); }; template struct map_caster { @@ -127,7 +127,7 @@ template struct map_caster { return d.release(); } - PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name() + _(", ") + value_conv::name() + _("]")); + PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]")); }; template struct list_caster { @@ -168,7 +168,7 @@ public: return l.release(); } - PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name() + _("]")); + PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]")); }; template struct type_caster> @@ -222,7 +222,7 @@ public: return l.release(); } - PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name() + _(_(""), _("[") + _() + _("]")) + _("]")); + PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _(_(""), _("[") + _() + _("]")) + _("]")); }; template struct type_caster> @@ -268,7 +268,7 @@ template struct optional_caster { return true; } - PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]")); + PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); }; #if PYBIND11_HAS_OPTIONAL @@ -348,7 +348,7 @@ struct variant_caster> { } using Type = V; - PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name()...) + _("]")); + PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name...) + _("]")); }; #if PYBIND11_HAS_VARIANT diff --git a/tests/test_copy_move.cpp b/tests/test_copy_move.cpp index 94113e3af..98d5e0a0b 100644 --- a/tests/test_copy_move.cpp +++ b/tests/test_copy_move.cpp @@ -86,7 +86,7 @@ template <> struct type_caster { protected: CopyOnlyInt value; public: - static PYBIND11_DESCR name() { return _("CopyOnlyInt"); } + static constexpr auto name = _("CopyOnlyInt"); bool load(handle src, bool) { value = CopyOnlyInt(src.cast()); return true; } static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {