diff --git a/include/pybind11/detail/descr.h b/include/pybind11/detail/descr.h index 635614b0d6..be0eb078c8 100644 --- a/include/pybind11/detail/descr.h +++ b/include/pybind11/detail/descr.h @@ -89,7 +89,19 @@ constexpr enable_if_t const_name(const T1 &, const T2 &d) { return d; } +#if defined(PYBIND11_CPP17) + +template ::value, int>::type = 0> +auto constexpr const_name() { + return const_name("True", "False"); +} + +template ::value, int>::type = 0> +#else template +#endif auto constexpr const_name() -> remove_cv_t::digits)> { return int_to_str::digits; } @@ -126,10 +138,23 @@ constexpr enable_if_t _(const T1 &d1, const T2 &d2) { return const_name(d1, d2); } +# if defined(PYBIND11_CPP17) + +template ::value, int>::type = 0> +auto constexpr _() { + return const_name(); +} + +template ::value, int>::type = 0> +# else template +# endif auto constexpr _() -> remove_cv_t::digits)> { return const_name(); } + template constexpr descr<1, Type> _() { return const_name(); diff --git a/include/pybind11/typing.h b/include/pybind11/typing.h index 405ff8714a..e9054c7843 100644 --- a/include/pybind11/typing.h +++ b/include/pybind11/typing.h @@ -102,17 +102,14 @@ class Never : public none { #if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911L # define PYBIND11_TYPING_H_HAS_STRING_LITERAL + +// Used for TypeVars and Strings, bytes, Enums, None for Literal object template struct StringLiteral { constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, name); } char name[N]; }; -template -class Literal : public object { - PYBIND11_OBJECT_DEFAULT(Literal, object, PyObject_Type) -}; - // Example syntax for creating a TypeVar. // typedef typing::TypeVar<"T"> TypeVarT; template @@ -122,6 +119,13 @@ class TypeVar : public object { }; #endif +#if defined(PYBIND11_CPP17) +template +class Literal : public object { + PYBIND11_OBJECT_DEFAULT(Literal, object, PyObject_Type) +}; +#endif + PYBIND11_NAMESPACE_END(typing) PYBIND11_NAMESPACE_BEGIN(detail) @@ -277,16 +281,25 @@ struct handle_type_name { }; #if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL) -template +template ::value, int>::type = 0> +auto constexpr const_name() { + return const_name(StrLit.name); +} + +template +struct handle_type_name> { + static constexpr auto name = const_name(); +}; +#endif + +#if defined(PYBIND11_CPP17) +template struct handle_type_name> { static constexpr auto name = const_name("Literal[") - + pybind11::detail::concat(const_name(Literals.name)...) + + pybind11::detail::concat(const_name()...) + const_name("]"); }; -template -struct handle_type_name> { - static constexpr auto name = const_name(StrLit.name); -}; #endif PYBIND11_NAMESPACE_END(detail) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 1764ccda02..e47c8b7366 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -122,14 +122,14 @@ void m_defs(py::module_ &m) { namespace literals { enum Color { RED = 0, BLUE = 1 }; -typedef py::typing::Literal<"26", - "0x1A", - "\"hello world\"", - "b\"hello world\"", - "u\"hello world\"", - "True", - "Color.RED", - "None"> +typedef py::typing::Literal<26, + 0x14, + py::typing::StringLiteral("\"hello world\""), + py::typing::StringLiteral("b\"hello world\""), + py::typing::StringLiteral("u\"hello world\""), + true, + py::typing::StringLiteral("Color.RED"), + py::typing::StringLiteral("None")> LiteralFoo; } // namespace literals namespace typevar { @@ -969,7 +969,8 @@ TEST_SUBMODULE(pytypes, m) { .value("RED", literals::Color::RED) .value("BLUE", literals::Color::BLUE); - m.def("annotate_literal", [](literals::LiteralFoo &o) -> py::object { return o; }); + m.def("annotate_complete_literal", [](literals::LiteralFoo &o) -> py::object { return o; }); + m.def("annotate_generic_containers", [](const py::typing::List &l) -> py::typing::List { return l; @@ -983,6 +984,15 @@ TEST_SUBMODULE(pytypes, m) { m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = false; #endif +#if defined(PYBIND11_CPP17) + m.def("annotate_literal", + [](py::typing::Literal<3, 6, 1, 0, true, false, 0x14> &o) -> py::object { return o; }); + + m.attr("PYBIND11_CPP17") = true; +#else + m.attr("PYBIND11_CPP17") = false; +#endif + #if defined(PYBIND11_TEST_PYTYPES_HAS_RANGES) // test_tuple_ranges diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index b6e64b9bf6..6953ee7b19 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -1036,13 +1036,24 @@ def test_optional_object_annotations(doc): @pytest.mark.skipif( - not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL, - reason="C++20 non-type template args feature not available.", + not m.PYBIND11_CPP17, + reason="C++17 auto template args feature not available.", ) def test_literal(doc): assert ( doc(m.annotate_literal) - == 'annotate_literal(arg0: Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]) -> object' + == "annotate_literal(arg0: Literal[3, 6, 1, 0, True, False, 20]) -> object" + ) + + +@pytest.mark.skipif( + not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL, + reason="C++20 non-type template args feature not available.", +) +def test_complete_literal(doc): + assert ( + doc(m.annotate_complete_literal) + == 'annotate_complete_literal(arg0: Literal[26, 20, "hello world", b"hello world", u"hello world", True, Color.RED, None]) -> object' )