fold with a fusion::vector initialized state

212 views Asked by At

Applying boost::fusion::fold with a std::vector initial state works fine.

#include <vector>
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/include/fold.hpp>
struct A;
struct B;
struct C;
int ia = 1, ib = 2, ic = 3;
namespace fusion = boost::fusion;

template< typename StdInteratorT>
struct initialize_map_with_std_vector 
{
    template<class T>
    StdInteratorT operator()(StdInteratorT& i, T& val) {
        val = *i;
        return ++i;
    }
};

void use_fold_with_std_vector() 
{
    using M = fusion::map<fusion::pair<A, int>, fusion::pair<B, char>, fusion::pair<C, double>>;
    M fm;
    std::vector<int> sv = { ia, ib, ic };
    auto state = sv.begin();
    fusion::fold(fm, state, initialize_map_with_std_vector<std::vector<int>::iterator>());
}

int main() { use_fold_with_std_vector(); }

When the same notion is applied to a fusion::vector however, the code fails to compile with the error no instance of overloaded function "boost::fusion::fold" matches the argument

    #include <vector>
    #include <boost/fusion/container/map.hpp>
    #include <boost/fusion/container/vector.hpp>
    #include <boost/fusion/include/fold.hpp>
    #include <boost/fusion/algorithm/iteration/fold.hpp>
    namespace fusion = boost::fusion;

    struct A;
    struct B;
    struct C;
    std::vector<int> va = { 4, 5, 6 };
    std::vector<char> vb = { 'a', 'b', 'c' };
    std::vector<double> vc = { 10., 11., 12. };    

    template< typename FusionIteratorT, typename Ret>
    struct initialize_map_with_fusion_vector {

        template<typename T>
        Ret operator()(FusionIteratorT& i,  T& val) {
            val = fusion::deref(i);
            return fusion::next(i);
        } 
    };

    void use_fold_with_fusion_vector() {
        using M = fusion::map<fusion::pair<A, std::vector<int>>, fusion::pair<B, std::vector<char>>, fusion::pair<C, std::vector<double>>>;
        using V = fusion::vector<std::vector<int>, std::vector<char>, std::vector<double>>;
        auto fm = M();
        auto fv = V(va, vb, vc);
        using FusionIteratorT = decltype(fusion::begin(fv));
        using Ret = fusion::result_of::next<fusion::result_of::begin<decltype(fv)>>;
        fusion::fold(fm, fusion::begin(fv),
            initialize_map_with_fusion_vector<FusionIteratorT, Ret > ());
}

int main() {use_fold_with_fusion_vector();}

How can I make fusion::fold work with a fusion::vector initializer?

1

There are 1 answers

1
Jason Rice On

One thing to note about using Boost.Hana for this is that hana::Foldable requires the length of the container be known at compile-time, so std::vector does not work well here without a run-time check.

Consider this example:

#include <boost/hana.hpp>
#include <iostream>
#include <stdexcept>
#include <vector>

namespace hana = boost::hana;

struct A { };
struct B { };
struct C { };

constexpr auto keys = hana::make_tuple(
  hana::type_c<A>,
  hana::type_c<B>,
  hana::type_c<C>
);

template <typename Keys, typename T>
auto make_map_from_vector(Keys keys, std::vector<T> const& v)
{
  // Run-time check of vector length
  if (v.size() != hana::length(keys))
    throw std::runtime_error(
      "I should've used std::array instead of std::vector! "
      "The length of std::array is known at compile-time."
    );

  return hana::unpack(
    hana::make_range(hana::size_c<0>, hana::length(keys)),
    [&](auto ...i)
    {
      return hana::make_map(
        hana::make_pair(hana::at(keys, i), v[i])...
      );
    }
  );
}

int main()
{
  auto my_map = make_map_from_vector(keys, std::vector<char>{'a', 'b', 'c'});
  std::cout << "A => " << my_map[hana::type_c<A>] << '\n';
  std::cout << "B => " << my_map[hana::type_c<B>] << '\n';
  std::cout << "C => " << my_map[hana::type_c<C>] << '\n';

  // explosions
  make_map_from_vector(keys, std::vector<char>{'a', 'b'});
}

output:

A => a
B => b
C => c
terminate called after throwing an instance of 'std::runtime_error'
  what():  I should've used std::array instead of std::vector! The length of std::array is known at compile-time.
Aborted (core dumped)