Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an equivalent of View concept from Boost.Fusion in Boost.Hana?

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
});
like image 355
lizarisk Avatar asked Mar 13 '23 06:03

lizarisk


2 Answers

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_wrappers 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!

like image 105
Louis Dionne Avatar answered Mar 30 '23 00:03

Louis Dionne


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

like image 39
Jason Rice Avatar answered Mar 29 '23 23:03

Jason Rice