I was trying to iterate over a user-defined struct with hana::for_each
and noticed that it gets copied/moved, while Boost.Fusion
allows you to iterate on the original struct in-place.
I didn't find anything like a View
concept from Boost.Fusion
in Boost.Hana
. How do I apply transformations to sequences without copying/moving them every time?
#include <boost/hana.hpp>
#include <iostream>
struct Foo {
Foo() = default;
Foo(const Foo&) { std::cout << "copy" << std::endl; }
Foo(Foo&&) { std::cout << "move" << std::endl; }
};
struct Struct {
BOOST_HANA_DEFINE_STRUCT(Struct,
(Foo, foo)
);
};
int main() {
Struct s;
auto m = boost::hana::members(s); // copy constructor invoked
}
UPDATE: I tried to use hana::transform
to apply std::ref
to the members, but Struct
is not a Functior
, so transform
is not applicable in this case. I was able to achieve the desired behavior using hana::accessors
, but it looks a bit hacky to me. I wish there was a way to create views.
hana::for_each(hana::accessors<Struct>(), [&s](const auto& accessor) {
const auto& member = hana::second(accessor)(s); // No copying
});
Jason is correct in saying that there is experimental support for views in Hana. The problem you encounter is a common one, and Hana does not currently address it in an elegant way. I hope that adding views to Hana will make this problem go away by providing both by-value and by-reference semantics to all algorithms where that makes sense. But adding this in a coherent manner is non-trivial, hence the experimental state of the feature.
That being said, you can solve your problem by either using accessors (as you show in your question), or by iterating on the keys associated to the members of the struct instead:
#include <boost/hana.hpp>
#include <iostream>
namespace hana = boost::hana;
struct Foo {
Foo() = default;
Foo(const Foo&) { std::cout << "copy" << std::endl; }
Foo(Foo&&) { std::cout << "move" << std::endl; }
};
struct Struct {
BOOST_HANA_DEFINE_STRUCT(Struct,
(Foo, foo)
);
};
int main() {
Struct s;
hana::for_each(hana::keys(s), [&s](const auto& key) {
const auto& member = hana::at_key(s, key); // No copying
});
}
If you want to manipulate the members themselves as a sequence, but do not want to copy them, you can create a sequence of references as follows:
#include <boost/hana.hpp>
#include <iostream>
namespace hana = boost::hana;
struct Foo {
Foo() = default;
Foo(const Foo&) { std::cout << "copy" << std::endl; }
Foo(Foo&&) { std::cout << "move" << std::endl; }
};
struct Struct {
BOOST_HANA_DEFINE_STRUCT(Struct,
(Foo, foo)
);
};
int main() {
Struct s;
auto members = hana::transform(hana::keys(s), [&s](auto const& key) {
return std::ref(hana::at_key(s, key));
});
}
members
will then be a sequence of std::reference_wrapper
s that you can carry around, but you'll have to use .get()
when accessing them. In a future version of Hana, hana::members
would probably return a view of key/value pairs, where the value is a reference to the original member. But there are a couple of issues that must be resolved before this is possible.
Hope this helps!
As far as views are concerned, there are some undocumented features in boost/hana/experimental/view.hpp
so they certainly appear to be on the horizon for Boost.Hana.
The current problem with Struct
is that it is Foldable
on key value pairs. In hana::unpack
it will put each value from your struct in a pair, creating a copy. Your solution using hana::accessors
is currently the way to go IMO.
Note that there is currently an open issue for this: https://github.com/boostorg/hana/issues/175
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With