mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 05:05:11 +00:00
Add syntax sugar for resolving overloaded functions (#541)
This commit is contained in:
parent
ae185b7f19
commit
4e959c9af4
@ -104,6 +104,8 @@ With the above change, the same Python code now produces the following output:
|
|||||||
>>> print(p)
|
>>> print(p)
|
||||||
<example.Pet named 'Molly'>
|
<example.Pet named 'Molly'>
|
||||||
|
|
||||||
|
.. [#f1] Stateless closures are those with an empty pair of brackets ``[]`` as the capture object.
|
||||||
|
|
||||||
.. _properties:
|
.. _properties:
|
||||||
|
|
||||||
Instance and static fields
|
Instance and static fields
|
||||||
@ -337,6 +339,35 @@ The overload signatures are also visible in the method's docstring:
|
|||||||
|
|
|
|
||||||
| Set the pet's name
|
| 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::
|
.. note::
|
||||||
|
|
||||||
To define multiple overloaded constructors, simply declare one after the
|
To define multiple overloaded constructors, simply declare one after the
|
||||||
@ -406,5 +437,3 @@ typed enums.
|
|||||||
...
|
...
|
||||||
|
|
||||||
By default, these are omitted to conserve space.
|
By default, these are omitted to conserve space.
|
||||||
|
|
||||||
.. [#f1] Stateless closures are those with an empty pair of brackets ``[]`` as the capture object.
|
|
||||||
|
@ -557,4 +557,39 @@ PYBIND11_DECL_FMT(bool, "?");
|
|||||||
/// Dummy destructor wrapper that can be used to expose classes with a private destructor
|
/// Dummy destructor wrapper that can be used to expose classes with a private destructor
|
||||||
struct nodelete { template <typename T> void operator()(T*) { } };
|
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)
|
NAMESPACE_END(pybind11)
|
||||||
|
@ -23,6 +23,9 @@ std::string test_function3(int i) {
|
|||||||
return "test_function(" + std::to_string(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() {
|
py::bytes return_bytes() {
|
||||||
const char *data = "\x01\x00\x02\x00";
|
const char *data = "\x01\x00\x02\x00";
|
||||||
return std::string(data, 4);
|
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_function2);
|
||||||
m.def("test_function", &test_function3);
|
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")
|
py::enum_<MyEnum>(m, "MyEnum")
|
||||||
.value("EFirstEntry", EFirstEntry)
|
.value("EFirstEntry", EFirstEntry)
|
||||||
.value("ESecondEntry", ESecondEntry)
|
.value("ESecondEntry", ESecondEntry)
|
||||||
|
@ -14,6 +14,9 @@ def test_function_overloading():
|
|||||||
assert test_function(MyEnum.EFirstEntry) == "test_function(enum=1)"
|
assert test_function(MyEnum.EFirstEntry) == "test_function(enum=1)"
|
||||||
assert test_function(MyEnum.ESecondEntry) == "test_function(enum=2)"
|
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():
|
def test_bytes():
|
||||||
from pybind11_tests import return_bytes, print_bytes
|
from pybind11_tests import return_bytes, print_bytes
|
||||||
|
@ -50,6 +50,11 @@ public:
|
|||||||
int *internal4() { return &value; } // return by pointer
|
int *internal4() { return &value; } // return by pointer
|
||||||
const int *internal5() { return &value; } // return by const 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;
|
int value = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,6 +122,17 @@ test_initializer methods_and_attributes([](py::module &m) {
|
|||||||
.def("internal3", &ExampleMandA::internal3)
|
.def("internal3", &ExampleMandA::internal3)
|
||||||
.def("internal4", &ExampleMandA::internal4)
|
.def("internal4", &ExampleMandA::internal4)
|
||||||
.def("internal5", &ExampleMandA::internal5)
|
.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("__str__", &ExampleMandA::toString)
|
||||||
.def_readwrite("value", &ExampleMandA::value)
|
.def_readwrite("value", &ExampleMandA::value)
|
||||||
;
|
;
|
||||||
|
@ -31,6 +31,11 @@ def test_methods_and_attributes():
|
|||||||
assert instance1.internal4() == 320
|
assert instance1.internal4() == 320
|
||||||
assert instance1.internal5() == 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
|
assert instance1.value == 320
|
||||||
instance1.value = 100
|
instance1.value = 100
|
||||||
assert str(instance1) == "ExampleMandA[value=100]"
|
assert str(instance1) == "ExampleMandA[value=100]"
|
||||||
|
Loading…
Reference in New Issue
Block a user