diff --git a/docs/classes.rst b/docs/classes.rst index 3e8f2ee97..872977684 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -104,6 +104,8 @@ With the above change, the same Python code now produces the following output: >>> print(p) +.. [#f1] Stateless closures are those with an empty pair of brackets ``[]`` as the capture object. + .. _properties: Instance and static fields @@ -337,6 +339,35 @@ The overload signatures are also visible in the method's docstring: | | Set the pet's name +If you have a C++14 compatible compiler [#cpp14]_, you can use an alternative +syntax to cast the overloaded function: + +.. code-block:: cpp + + py::class_(m, "Pet") + .def("set", py::overload_cast(&Pet::set), "Set the pet's age") + .def("set", py::overload_cast(&Pet::set), "Set the pet's name"); + +Here, ``py::overload_cast`` only requires the parameter types to be specified. +The return type and class are deduced. This avoids the additional noise of +``void (Pet::*)()`` as seen in the raw cast. If a function is overloaded based +on constness, the ``py::const_`` tag should be used: + +.. code-block:: cpp + + struct Widget { + int foo(int x, float y); + int foo(int x, float y) const; + }; + + py::class_(m, "Widget") + .def("foo_mutable", py::overload_cast(&Widget::foo)) + .def("foo_const", py::overload_cast(&Widget::foo, py::const_)); + + +.. [#cpp14] A compiler which supports the ``-std=c++14`` flag + or Visual Studio 2015 Update 2 and newer. + .. note:: To define multiple overloaded constructors, simply declare one after the @@ -406,5 +437,3 @@ typed enums. ... By default, these are omitted to conserve space. - -.. [#f1] Stateless closures are those with an empty pair of brackets ``[]`` as the capture object. diff --git a/include/pybind11/common.h b/include/pybind11/common.h index 72e8e33e3..712c1a5d6 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -557,4 +557,39 @@ PYBIND11_DECL_FMT(bool, "?"); /// Dummy destructor wrapper that can be used to expose classes with a private destructor struct nodelete { template void operator()(T*) { } }; +// overload_cast requires variable templates: C++14 or MSVC 2015 Update 2 +#if defined(PYBIND11_CPP14) || _MSC_FULL_VER >= 190023918 +#define PYBIND11_OVERLOAD_CAST 1 + +NAMESPACE_BEGIN(detail) +template +struct overload_cast_impl { + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) { return pf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + -> decltype(pmf) { return pmf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + -> decltype(pmf) { return pmf; } +}; +NAMESPACE_END(detail) + +/// Syntax sugar for resolving overloaded function pointers: +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func) +template +static constexpr detail::overload_cast_impl overload_cast = {}; +// MSVC 2015 only accepts this particular initialization syntax for this variable template. + +/// Const member function selector for overload_cast +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func, const_) +static constexpr auto const_ = std::true_type{}; + +#endif // overload_cast + NAMESPACE_END(pybind11) diff --git a/tests/test_constants_and_functions.cpp b/tests/test_constants_and_functions.cpp index 29a27965a..c8c0392c9 100644 --- a/tests/test_constants_and_functions.cpp +++ b/tests/test_constants_and_functions.cpp @@ -23,6 +23,9 @@ std::string test_function3(int i) { return "test_function(" + std::to_string(i) + ")"; } +py::str test_function4(int, float) { return "test_function(int, float)"; } +py::str test_function4(float, int) { return "test_function(float, int)"; } + py::bytes return_bytes() { const char *data = "\x01\x00\x02\x00"; return std::string(data, 4); @@ -45,6 +48,14 @@ test_initializer constants_and_functions([](py::module &m) { m.def("test_function", &test_function2); m.def("test_function", &test_function3); +#if defined(PYBIND11_OVERLOAD_CAST) + m.def("test_function", py::overload_cast(&test_function4)); + m.def("test_function", py::overload_cast(&test_function4)); +#else + m.def("test_function", static_cast(&test_function4)); + m.def("test_function", static_cast(&test_function4)); +#endif + py::enum_(m, "MyEnum") .value("EFirstEntry", EFirstEntry) .value("ESecondEntry", ESecondEntry) diff --git a/tests/test_constants_and_functions.py b/tests/test_constants_and_functions.py index 2c6321e05..d13d3af1b 100644 --- a/tests/test_constants_and_functions.py +++ b/tests/test_constants_and_functions.py @@ -14,6 +14,9 @@ def test_function_overloading(): assert test_function(MyEnum.EFirstEntry) == "test_function(enum=1)" assert test_function(MyEnum.ESecondEntry) == "test_function(enum=2)" + assert test_function(1, 1.0) == "test_function(int, float)" + assert test_function(2.0, 2) == "test_function(float, int)" + def test_bytes(): from pybind11_tests import return_bytes, print_bytes diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index ab59d8c21..11fc90091 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -50,6 +50,11 @@ public: int *internal4() { return &value; } // return by pointer const int *internal5() { return &value; } // return by const pointer + py::str overloaded(int, float) { return "(int, float)"; } + py::str overloaded(float, int) { return "(float, int)"; } + py::str overloaded(int, float) const { return "(int, float) const"; } + py::str overloaded(float, int) const { return "(float, int) const"; } + int value = 0; }; @@ -117,6 +122,17 @@ test_initializer methods_and_attributes([](py::module &m) { .def("internal3", &ExampleMandA::internal3) .def("internal4", &ExampleMandA::internal4) .def("internal5", &ExampleMandA::internal5) +#if defined(PYBIND11_OVERLOAD_CAST) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded", py::overload_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) +#else + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) +#endif .def("__str__", &ExampleMandA::toString) .def_readwrite("value", &ExampleMandA::value) ; diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 28c0de538..2b0f8d571 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -31,6 +31,11 @@ def test_methods_and_attributes(): assert instance1.internal4() == 320 assert instance1.internal5() == 320 + assert instance1.overloaded(1, 1.0) == "(int, float)" + assert instance1.overloaded(2.0, 2) == "(float, int)" + assert instance1.overloaded_const(3, 3.0) == "(int, float) const" + assert instance1.overloaded_const(4.0, 4) == "(float, int) const" + assert instance1.value == 320 instance1.value = 100 assert str(instance1) == "ExampleMandA[value=100]"