-
Notifications
You must be signed in to change notification settings - Fork 216
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
Policy for returning sequences of references #175
Comments
Reading again my initial concerns about creating dangling references, I think it is misplaced to have this concern in the context of C++. I think returning a tuple of references is fine as long as
Hence, I have the following resolution in mind:
The reasoning here is that when Now, this reasoning applies to other functions that behave similarly. Right now, all the functions I can think of are:
It is likely that settling on a solution for |
To quote you from #90, regarding your snippet there:
I don't think that's annoying at all. I think it makes sense. If you want to copy members of a struct, just copy the struct. I think it's completely intuitive to say that
I agree.
For similar reasons as my last comment in #90, I tentatively argue that |
The #include <boost/hana.hpp>
#include <functional>
#include <utility>
namespace hana = boost::hana;
using namespace boost::hana::literals;
struct forward_as_tuple_t {
template<typename ...T>
constexpr auto operator()(T&&... t) const {
return hana::make_tuple(std::ref(std::forward<T>(t))...);
}
};
constexpr forward_as_tuple_t forward_as_tuple{};
struct members_t {
template <typename S>
constexpr auto operator()(S&& s) const {
return hana::members(std::forward<S>(s));
}
template <typename S, typename Fn>
constexpr auto operator()(S&& s, Fn&& fn) const {
return hana::unpack(hana::accessors<std::decay_t<S>>(),
[&](auto&&... x) {
return fn(
hana::second(std::forward<decltype(x)>(x))(s)...
);
}
);
}
};
constexpr members_t members{};
constexpr auto member_refs = hana::reverse_partial(members, ::forward_as_tuple);
struct Person {
BOOST_HANA_DEFINE_STRUCT(Person,
(int, age)
);
};
int main() {
Person john{30};
members(john, [](auto& age) { age = 35; });
BOOST_HANA_RUNTIME_ASSERT(john.age == 35);
auto refs = member_refs(john);
refs[0_c].get() = 40;
BOOST_HANA_RUNTIME_ASSERT(john.age == 40);
} This would preserve the existing interface, and those using |
Well, the issue of returning sequences that contain references does not only apply to |
@ldionne That's fine, but the idea is that they could all have an optional callback argument. Other than that perhaps functions like |
I came to a similar issue with #include <boost/hana.hpp>
#include <iostream>
namespace hana = boost::hana;
int main(int argc, const char** argv)
{
auto xs = hana::make<hana::tuple_tag>(1, 2, 3);
auto ys = hana::make<hana::tuple_tag>(1, 2, 3);
auto add_ref = [](auto& x, const auto& y){x+=y;}; // well formed lambda
auto add_refref = [](auto&& x, const auto& y){x+=y;}; // ill formed lambda required for working solution
auto ref = [](auto& x) {return std::ref(x);};
auto print = [](const auto& z){ std::cout << z << std::endl;};
// supposed to add values of ys ontop of xs
hana::for_each(hana::zip(xs,ys),hana::fuse(add_refref));
// Fails: prints 1 2 3 because the x that ends up in add_refref is a temporary.
hana::for_each(xs,print);
// does not compile because x will be a temporary.
// at least the compiler warns us that something is fishy
// candidate function [with $0 = int, $1 = int] not viable: expects an l-value for 1st argument
// hana::for_each(hana::zip(xs,ys),hana::fuse(add_ref));
// working solution but imo ugly
hana::for_each(hana::zip(hana::transform(xs,ref),ys),hana::fuse(add_refref));
hana::for_each(xs,print); // prints 2 4 6
// does not compile since x is still a temporary reference wrapper
// candidate function [with $0 = std::__1::reference_wrapper<int>, $1 = int] not viable: expects an l-value for 1st argument
// hana::for_each(hana::zip(hana::transform(xs,ref),ys),hana::fuse(add_ref));
} |
@aolsux #include<boost/hana.hpp>
namespace hana = boost::hana;
int main() {
auto xs = hana::make_tuple(1, 2, 3);
auto ys = hana::make_tuple(1, 2, 3);
hana::for_each(hana::range_c<int, 0, hana::length(xs)>,
[&](auto i) {
hana::at(xs, i) += hana::at(ys, i);
});
BOOST_HANA_RUNTIME_ASSERT(xs == hana::make_tuple(2, 4, 6));
} |
@ricejasonf yeah, that is more or less equal to the code based on my own small meta library which i wanted to replace by hana :D maybe if just written to much python code these days... |
How about a #include <boost/hana.hpp>
#include <memory>
#include <utility>
namespace hana = boost::hana;
using namespace boost::hana::literals;
struct Person {
BOOST_HANA_DEFINE_STRUCT(Person,
(int, age)
);
};
// this would actually be an impl for `hana::members`
struct forward_as_t {
template<typename S>
constexpr auto operator()(hana::members_t const&, S&& s) const {
return hana::unpack(hana::accessors<std::decay_t<S>>(),
[&](auto&&... x) {
return hana::make_tuple(
std::ref(hana::second(std::forward<decltype(x)>(x))(s))...
);
}
);
}
};
constexpr forward_as_t forward_as{};
constexpr auto forward_as_members = hana::partial(forward_as, hana::members);
// or maybe this way
// constexpr forward_as<hana::members_t> forward_as_members{};
int main() {
Person john{30};
forward_as(hana::members, john)[0_c].get() = 35;
BOOST_HANA_RUNTIME_ASSERT(john.age == 35);
forward_as_members(john)[0_c].get() = 40;
BOOST_HANA_RUNTIME_ASSERT(john.age == 40);
} |
@aolsux If auto ts = hana::make_tuple(1, 2, 3);
auto us = hana::make_tuple(4, 5, 6);
auto z = hana::zip(ts, us); // z does not 'own' its elements, which is not consistent While I very much understand the interest of being able to retain references, it's not the way Hana was designed. Instead, if you need references, I would do it explicitly: auto xs = hana::make_tuple(1, 2, 3);
auto ys = hana::make_tuple(1, 2, 3);
auto ref = [](auto& x) { return std::ref(x); };
hana::for_each(hana::zip(hana::transform(xs, ref), ys), [](auto x, auto const& y) {
x.get() += y;
}); It does require the additional steps of I do agree that the ability to form sequences of references is useful, and it has been requested many times. However, I think that we stand a better chance of doing this properly if we introduce something like a |
Actually, what about writing the following? auto xs = hana::make_tuple(1, 2, 3);
auto ys = hana::make_tuple(1, 2, 3);
xs = hana::zip_with(hana::plus, xs, ys); I'd be tempted to think that at least in simple cases, the optimizer would be able to do a good job and not move ints around. |
This issue is a spinoff of #90. Here, instead of considering algorithms that return values/references like
at
andat_key
, we consider algorithms that return containers that should probably hold references likefind_if
andmembers
. I'm splitting #90 into two issues because I think the solution for both will be slightly different, even though related.To illustrate the issue, consider the following code, which was submitted to me in a private email:
This is because
members
returns a container holding copies of the members of the struct, not references to it. While it seems that it would be more useful to return a sequence of references, it would also create lifetime issues when thePerson
is a temporary.The text was updated successfully, but these errors were encountered: