Question

I want to be able to assign to a boost::fusion::map of references values from both:

  • a boost::fusion::map of values, and
  • a boost::fusion::map of references.

What is the right (generic and idiomatic) way to do this?

// Let:
using bf = boost::fusion;
struct A {}; struct B{};

// I have a map of references
using my_ref_map = bf::map<bf::pair<A, float&>, bf::pair<B, int&>>;

// and a map of values:
using my_val_map = bf::map<bf::pair<A, float>, bf::pair<B, int>>;

// Say I have two values and I make a map of references
float a; int b;
my_ref_map MyRefMap(bf::make_pair<A>(a), bf::make_pair<B>(b));

// Then I wanto to set a and b using both:
// a map of values:
my_val_map MyValMap(bf::make_pair<A>(2.0), bf::make_pair<B>(3))

// and a map of references:
float aSrc = 2.0; int bSrc = 3;
my_ref_map MyRefMap(bf::make_pair<A>(aSrc), bf::make_pair<B>(bSrc));

// ... some code ...  (see below for the things I've tried)

assert(a == 2.0 && b == 3);  // <- End result.

I've tried the following:

bf::copy(MyValMap, MyRefMap);
// copy complains that bf::pair<A, float&> cannot be assigned 
// because its copy assignment operator is implicitly deleted.
// This is fine, I wasn't expecting copy to work here.

Implement a bf::zip_non_const (see below) that allows you to modify the map and do:

bf::for_each(bf::zip_non_const(MyRefMap, MyValMap), [](auto i) {
  bf::at_c<0>(i) = bf::at_c<1>(i); });
// This works but bf::zip returns const& for a reason:
// there has to be a better way.

This is my implementation of zip_non_const:

namespace boost { namespace fusion {
// Boilerplate:
namespace result_of {
template<class... Ts> struct zip_non_const {
  using sequences = mpl::vector<Ts...>;
  using ref_params 
    = typename mpl::transform<sequences, add_reference<mpl::_> >::type;
  using type = zip_view<typename result_of::as_vector<ref_params>::type>;
};
}

// zip_non_const
template<class... Ts>
inline typename result_of::zip_non_const<Ts...>::type zip_non_const(Ts&&... ts)
{ return {fusion::vector<Ts&&...>(ts...)}; }

// swap for fusion types
template <class T> inline void swap(T&& lhs, T&& rhs) noexcept {
  using std::swap;
  std::remove_reference_t<T> tmp = lhs;
  lhs = rhs;
  rhs = tmp;
}

}  // namespace fusion
}  // namespace boost
Was it helpful?

Solution

fusion::map defines a = operator that allows assignment. This works fine in the case when you assign a my_val_map to a my_ref_map, but fails with the same error you encountered with fusion::copy when the assignment is between two my_ref_maps. In the code below I simply iterate over the pairs in the first map, and then assign the data to the corresponding pair in the destination map.
It is important that the destination map have all the keys that are present in the map that it is being copied from, otherwise you'll get a compilation error. (if you have Map1=map<pair<A,int> >; Map2=map<pair<A,int>, pair<B,float> >; you can copy from Map1 to Map2, but not from Map2 to Map1).
Live Example

#include <iostream>

#include <boost/fusion/include/map.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/at_key.hpp>

namespace fusion = boost::fusion;
struct A {}; struct B{};

// I have a map of references
using my_ref_map = fusion::map<fusion::pair<A, float&>, fusion::pair<B, int&>>;

// and a map of values:
using my_val_map = fusion::map<fusion::pair<A, float>, fusion::pair<B, int>>;

template<typename MapOut>
struct map_assigner //you could use a c++14 lambda if your compiler supports it
{
    map_assigner(MapOut& map):map_out(map){}

    template <typename Pair>
    void operator()(const Pair& pair) const
    {
        fusion::at_key<typename Pair::first_type>(map_out) = pair.second;
    }

    MapOut& map_out;
};

template <typename MapIn, typename MapOut>
void my_copy(const MapIn& map_in, MapOut& map_out)
{
    fusion::for_each(map_in,map_assigner<MapOut>(map_out)); 
}


int main()
{

    // Say I have two values and I make a map of references
    float a=0.0f;
    int b=0;
    my_ref_map MyRefMap(fusion::make_pair<A,float&>(a), fusion::make_pair<B,int&>(b));

    // Then I wanto to set a and b using both:
    // a map of values:
    my_val_map MyValMap(fusion::make_pair<A>(2.0f), fusion::make_pair<B>(3));

    // and a map of references:
    float aSrc = 4.0f; int bSrc = 6;
    my_ref_map MyRefMap2(fusion::make_pair<A,float&>(aSrc), fusion::make_pair<B,int&>(bSrc));

    my_copy(MyValMap,MyRefMap);

    std::cout << "a=" << a << ", b=" << b << std::endl;  // <- End result.

    my_copy(MyRefMap2,MyRefMap);

    std::cout << "a=" << a << ", b=" << b << std::endl;  // <- End result.
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top