-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Adjusting type_caster<std::reference_wrapper<T>>
to support const/non-const propagation in cast_op
.
#2705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adjusting type_caster<std::reference_wrapper<T>>
to support const/non-const propagation in cast_op
.
#2705
Changes from 26 commits
65abb62
c603907
5cdc0ca
35e7fa5
4cd06d3
5abd823
c5bd104
f0e41f5
b56a568
d71cd7a
3c69635
8f32ba3
d144e59
a7ec72e
e1aca4b
005d3fe
4ac23ca
a3d9b18
5b4fb98
8dcbc42
9f77862
7672e92
c747ae2
33f57cc
d869976
f109edd
775a7c9
6f6dac7
fd6dc5b
b3ecc53
c22772b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -960,9 +960,15 @@ template <typename type> class type_caster<std::reference_wrapper<type>> { | |||||||||||||||||||
private: | ||||||||||||||||||||
using caster_t = make_caster<type>; | ||||||||||||||||||||
caster_t subcaster; | ||||||||||||||||||||
using subcaster_cast_op_type = typename caster_t::template cast_op_type<type>; | ||||||||||||||||||||
static_assert(std::is_same<typename std::remove_const<type>::type &, subcaster_cast_op_type>::value, | ||||||||||||||||||||
"std::reference_wrapper<T> caster requires T to have a caster with an `T &` operator"); | ||||||||||||||||||||
using reference_t = typename std::add_lvalue_reference<type>::type; | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be more specific, According to cppreference.com, this is where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||||||||||||
using subcaster_cast_op_type = | ||||||||||||||||||||
laramiel marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||
typename caster_t::template cast_op_type<reference_t>; | ||||||||||||||||||||
|
||||||||||||||||||||
static_assert((std::is_same<typename std::remove_const<type>::type &, | ||||||||||||||||||||
subcaster_cast_op_type>::value || | ||||||||||||||||||||
std::is_same<reference_t, subcaster_cast_op_type>::value), | ||||||||||||||||||||
"std::reference_wrapper<T> caster requires T to have a caster " | ||||||||||||||||||||
"with an operator `T &` or `const T&`"); | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this line isn't as long as this one below:
It's probably more clear if we put it like this? (also with correct indentation and with
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||||||||||||
public: | ||||||||||||||||||||
bool load(handle src, bool convert) { return subcaster.load(src, convert); } | ||||||||||||||||||||
static constexpr auto name = caster_t::name; | ||||||||||||||||||||
|
@@ -973,7 +979,7 @@ template <typename type> class type_caster<std::reference_wrapper<type>> { | |||||||||||||||||||
return caster_t::cast(&src.get(), policy, parent); | ||||||||||||||||||||
} | ||||||||||||||||||||
template <typename T> using cast_op_type = std::reference_wrapper<type>; | ||||||||||||||||||||
operator std::reference_wrapper<type>() { return subcaster.operator subcaster_cast_op_type&(); } | ||||||||||||||||||||
operator std::reference_wrapper<type>() { return subcaster.operator subcaster_cast_op_type(); } | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
or even (depending on the other discussion)
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So you already listed point 3/ where I removed a & based on review feedback as a "separate change"; how is this not the same? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean? I don't mean this should be taken out, but if you go and change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you don't agree with the suggestion in #2705 (comment), that's also fine with me; then just argue why not and do not do it! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, yes. I just saw this being used in other casters. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm also fine with not applying this change; it's supposed to do exactly the same, though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||||||||||||
}; | ||||||||||||||||||||
|
||||||||||||||||||||
#define PYBIND11_TYPE_CASTER(type, py_name) \ | ||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
tests/test_const_ref_caster.cpp | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests seem more appropriate in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps, but I think that since they are intended to exercise different aspects, putting it into a separate file makes the intent a bit more clear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, pybind11's tests are a mess. But I think the idea it to keep a minimal amount of files, because they all take quite some time to compile. I also don't think this one test is enough to create a separate file for. Meanwhile, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other counter point is that this requires a custom type caster; so maybe move them to custom_type_casters.cc? I think that the current test is about as minimal as it can be while exercising the various paths we want to test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just logging my view, not a request: it makes me sad to see this test lumped in with other things. I know the existing tests do that a lot, but I think it really isn't a good approach, because it convolutes unrelated things, makes them harder to understand/follow, and obviously is counter the the idea of testing small self-contained units. Sticking with the established pattern only has so much value. Nudging things in a better direction has value, too. My preference is definitely to keep this test separate, if Boris or Yannick can agree. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Anyway, Done. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @rwgk however I will change it to the reviewers consensus. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, coming from this PR, it's testing the built-in caster for
It's not really about the custom caster, though. It's about testing
Why exactly? Because I had explicitly tried to design/shrink this to be as minimal, unit-testy as possible, such that it could fit in with the rest of the And to me it's more confusing to break convention than to add a few lines to the existing tests. I do agree the tests are messy, btw, but I don't see the urgency of splitting things up. Build times are (partially) to blame, I think. So maybe once #2445 gets merged, it will be more feasible to split up tests, in a separate PR that brings order to the tests in a less ad-hoc fashion?
Not sure there's consensus, though. If you want, we can give @henryiii a decisive vote, but ... well, yeah, I've explained my reasoning :-) |
||
|
||
This test verifies that const-ref is propagated through type_caster cast_op. | ||
|
||
The returned ConstRefCasted type is a mimimal type that is constructed to | ||
reference the casting mode used. | ||
*/ | ||
|
||
#include <pybind11/complex.h> | ||
|
||
#include "pybind11_tests.h" | ||
|
||
|
||
struct ConstRefCasted { | ||
bool is_const; | ||
bool is_ref; | ||
}; | ||
|
||
PYBIND11_NAMESPACE_BEGIN(pybind11) | ||
PYBIND11_NAMESPACE_BEGIN(detail) | ||
template <> | ||
class type_caster<ConstRefCasted> { | ||
public: | ||
static constexpr auto name = _<ConstRefCasted>(); | ||
|
||
bool load(handle, bool) { return true; } | ||
|
||
operator ConstRefCasted&&() { value = {false, false}; return std::move(value); } | ||
operator ConstRefCasted&() { value = {false, true}; return value; } | ||
operator ConstRefCasted*() { value = {false, false}; return &value; } | ||
|
||
operator const ConstRefCasted&() { value = {true, true}; return value; } | ||
operator const ConstRefCasted*() { value = {true, false}; return &value; } | ||
|
||
// | ||
template <typename T_> | ||
using cast_op_type = | ||
/// const | ||
conditional_t< | ||
std::is_same<remove_reference_t<T_>, const ConstRefCasted*>::value, const ConstRefCasted*, | ||
conditional_t< | ||
std::is_same<T_, const ConstRefCasted&>::value, const ConstRefCasted&, | ||
// non-const | ||
conditional_t< | ||
std::is_same<remove_reference_t<T_>, ConstRefCasted*>::value, ConstRefCasted*, | ||
conditional_t< | ||
std::is_same<T_, ConstRefCasted&>::value, ConstRefCasted&, | ||
/* else */ConstRefCasted&&>>>>; | ||
|
||
private: | ||
ConstRefCasted value = {false, false}; | ||
}; | ||
PYBIND11_NAMESPACE_END(detail) | ||
PYBIND11_NAMESPACE_END(pybind11) | ||
|
||
TEST_SUBMODULE(const_ref_caster, m) { | ||
py::class_<ConstRefCasted>(m, "ConstRefCasted", | ||
"A `py::class_` type for testing") | ||
.def(py::init<>()) | ||
.def_readonly("is_const", &ConstRefCasted::is_const) | ||
.def_readonly("is_ref", &ConstRefCasted::is_ref); | ||
|
||
|
||
m.def("takes", [](ConstRefCasted x) { | ||
return !x.is_const && !x.is_ref; | ||
}); | ||
m.def("takes_ptr", [](ConstRefCasted* x) { | ||
return !x->is_const && !x->is_ref; | ||
}); | ||
m.def("takes_ref", [](ConstRefCasted& x) { | ||
return !x.is_const && x.is_ref; | ||
}); | ||
m.def("takes_ref_wrap", [](std::reference_wrapper<ConstRefCasted> x) { | ||
return !x.get().is_const && x.get().is_ref; | ||
}); | ||
|
||
m.def("takes_const_ptr", [](const ConstRefCasted* x) { | ||
return x->is_const && !x->is_ref; | ||
}); | ||
m.def("takes_const_ref", [](const ConstRefCasted& x) { | ||
return x.is_const && x.is_ref; | ||
}); | ||
m.def("takes_const_ref_wrap", | ||
[](std::reference_wrapper<const ConstRefCasted> x) { | ||
return x.get().is_const && x.get().is_ref; | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# -*- coding: utf-8 -*- | ||
import pytest | ||
|
||
import env # noqa: F401 | ||
|
||
from pybind11_tests import const_ref_caster as m | ||
|
||
|
||
def test_takes(): | ||
assert m.takes(m.ConstRefCasted()) | ||
|
||
assert m.takes_ptr(m.ConstRefCasted()) | ||
assert m.takes_ref(m.ConstRefCasted()) | ||
assert m.takes_ref_wrap(m.ConstRefCasted()) | ||
|
||
assert m.takes_const_ptr(m.ConstRefCasted()) | ||
assert m.takes_const_ref(m.ConstRefCasted()) | ||
assert m.takes_const_ref_wrap(m.ConstRefCasted()) |
Uh oh!
There was an error while loading. Please reload this page.