Add syntax sugar for resolving overloaded functions (#541)

This commit is contained in:
Dean Moldovan 2016-12-08 11:07:52 +01:00 committed by Wenzel Jakob
parent ae185b7f19
commit 4e959c9af4
6 changed files with 101 additions and 2 deletions

View File

@ -104,6 +104,8 @@ With the above change, the same Python code now produces the following output:
>>> print(p)
<example.Pet named 'Molly'>
.. [#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_<Pet>(m, "Pet")
.def("set", py::overload_cast<int>(&Pet::set), "Set the pet's age")
.def("set", py::overload_cast<const std::string &>(&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_<Widget>(m, "Widget")
.def("foo_mutable", py::overload_cast<int, float>(&Widget::foo))
.def("foo_const", py::overload_cast<int, float>(&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.

View File

@ -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 <typename T> 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 <typename... Args>
struct overload_cast_impl {
template <typename Return>
constexpr auto operator()(Return (*pf)(Args...)) const noexcept
-> decltype(pf) { return pf; }
template <typename Return, typename Class>
constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept
-> decltype(pmf) { return pmf; }
template <typename Return, typename Class>
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<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
template <typename... Args>
static constexpr detail::overload_cast_impl<Args...> overload_cast = {};
// MSVC 2015 only accepts this particular initialization syntax for this variable template.
/// Const member function selector for overload_cast
/// - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
/// - sweet: overload_cast<Arg>(&Class::func, const_)
static constexpr auto const_ = std::true_type{};
#endif // overload_cast
NAMESPACE_END(pybind11)

View File

@ -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<int, float>(&test_function4));
m.def("test_function", py::overload_cast<float, int>(&test_function4));
#else
m.def("test_function", static_cast<py::str (*)(int, float)>(&test_function4));
m.def("test_function", static_cast<py::str (*)(float, int)>(&test_function4));
#endif
py::enum_<MyEnum>(m, "MyEnum")
.value("EFirstEntry", EFirstEntry)
.value("ESecondEntry", ESecondEntry)

View File

@ -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

View File

@ -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<int, float>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<float, int>(&ExampleMandA::overloaded))
.def("overloaded_const", py::overload_cast<int, float>(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", py::overload_cast<float, int>(&ExampleMandA::overloaded, py::const_))
#else
.def("overloaded", static_cast<py::str (ExampleMandA::*)(int, float)>(&ExampleMandA::overloaded))
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, int)>(&ExampleMandA::overloaded))
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, float) const>(&ExampleMandA::overloaded))
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, int) const>(&ExampleMandA::overloaded))
#endif
.def("__str__", &ExampleMandA::toString)
.def_readwrite("value", &ExampleMandA::value)
;

View File

@ -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]"