Skip to content

Commit

Permalink
Add lvalue ref-qualified cpp_function constructors (#2213)
Browse files Browse the repository at this point in the history
* added overload for l-value ref-qualified methods

* Added test.
Before, the code would have failed to build.
  • Loading branch information
clemenssielaff committed Jun 10, 2020
1 parent b524008 commit 63df87f
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 2 deletions.
22 changes: 20 additions & 2 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,38 @@ class cpp_function : public function {
(detail::function_signature_t<Func> *) nullptr, extra...);
}

/// Construct a cpp_function from a class method (non-const)
/// Construct a cpp_function from a class method (non-const, no ref-qualifier)
template <typename Return, typename Class, typename... Arg, typename... Extra>
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*) (Class *, Arg...)) nullptr, extra...);
}

/// Construct a cpp_function from a class method (const)
/// Construct a cpp_function from a class method (non-const, lvalue ref-qualifier)
/// A copy of the overload for non-const functions without explicit ref-qualifier
/// but with an added `&`.
template <typename Return, typename Class, typename... Arg, typename... Extra>
cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*) (Class *, Arg...)) nullptr, extra...);
}

/// Construct a cpp_function from a class method (const, no ref-qualifier)
template <typename Return, typename Class, typename... Arg, typename... Extra>
cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
}

/// Construct a cpp_function from a class method (const, lvalue ref-qualifier)
/// A copy of the overload for const functions without explicit ref-qualifier
/// but with an added `&`.
template <typename Return, typename Class, typename... Arg, typename... Extra>
cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) {
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
}

/// Return the function name
object name() const { return attr("__name__"); }

Expand Down
15 changes: 15 additions & 0 deletions tests/test_methods_and_attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ class RegisteredDerived : public UnregisteredBase {
double sum() const { return rw_value + ro_value; }
};

// Test explicit lvalue ref-qualification
struct RefQualified {
int value = 0;

void refQualified(int other) & { value += other; }
int constRefQualified(int other) const & { return value + other; }
};

TEST_SUBMODULE(methods_and_attributes, m) {
// test_methods_and_attributes
py::class_<ExampleMandA> emna(m, "ExampleMandA");
Expand Down Expand Up @@ -457,4 +465,11 @@ TEST_SUBMODULE(methods_and_attributes, m) {
m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); },
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
m.def("destruction_tester_cstats", &ConstructorStats::get<DestructionTester>, py::return_value_policy::reference);

// test_methods_and_attributes
py::class_<RefQualified>(m, "RefQualified")
.def(py::init<>())
.def_readonly("value", &RefQualified::value)
.def("refQualified", &RefQualified::refQualified)
.def("constRefQualified", &RefQualified::constRefQualified);
}
11 changes: 11 additions & 0 deletions tests/test_methods_and_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,3 +510,14 @@ def test_custom_caster_destruction():

# Make sure we still only have the original object (from ..._no_destroy()) alive:
assert cstats.alive() == 1


def test_ref_qualified():
"""Tests that explicit lvalue ref-qualified methods can be called just like their
non ref-qualified counterparts."""

r = m.RefQualified()
assert r.value == 0
r.refQualified(17)
assert r.value == 17
assert r.constRefQualified(23) == 40

0 comments on commit 63df87f

Please sign in to comment.