根据键合并两个 Boost Fusion 映射
Merge two Boost Fusion maps based on keys
我有两个 boost::fusion::map
我想以某种方式合并。对于这两个地图,我想生成第三个地图,它具有两个地图中都存在的所有键,如果两个地图都存在,则添加值。例如:
#include <boost/fusion/container/map.hpp>
namespace bfn = boost::fusion;
using namespace boost::fusion;
struct x{};
struct y{};
struct z{};
int main(){
auto m = make_map<x, y>(2, 4.3);
auto n = make_map<x>(2.);
auto l = accumulate_merge(m, n); // how this function should look like?
}
之后 l
将等同于 make_map<x, y>(2 + 2., 4.3)
.
我不知道从哪里开始。我尝试从 join
开始(并消除重复项,但我很快就变得复杂了)。
Boost Fusion 中是否有可以帮助我的工具?
(有很多微妙之处,比如如果对于同一个键,两个相应的类型不同——但仍然可以添加——。但是任何第一个版本都会有所帮助)。
我设法用 boost::fusion::fold
两次想出了这个版本。我不确定它是否是(编译时)最优的。
基本上,我先遍历第一张地图。如果键不在第二个映射中,则将元素推送到结果。如果键在第二个映射中,则添加元素和结果(无论将什么类型推送到结果)。
最后,我在第二张地图上进行了迭代。如果密钥在第一张地图中,我将忽略它(因为它是在第一遍中添加的)。如果键不在第二个中,则将元素推送到结果中。
(结果是第一个map优先于key类型的排序)
有一些未解决的问题或疑问:
1) 有更多 efficient/compact 方法可以做到这一点
2) 我不确定 forward
的用途,基本上我到处都用它(以防万一)
3) 不确定函数 returns 中的 auto
与 decltype(auto)
。
4) 这些函数对 SFINAE 不友好,我可以添加守卫来生成软错误。 (例如,如果无法添加值)。
5) 结果没有任何自然顺序,我不知道这是算法问题还是 fold 问题,或者因为 map 没有指定 push_back
的顺序(之后都是地图)。
Comments are welcome.
现在代码:
namespace boost{
namespace fusion{
namespace detail{
template<class Map2>
struct merge1{
Map2 m2_;
template<typename MapOut, typename Pair1>
auto operator()(MapOut&& mo, Pair1&& p1) const{
return conditional_push_back(std::forward<MapOut>(mo), std::forward<Pair1>(p1), has_key<typename std::decay_t<Pair1>::first_type>(m2_));
}
template<typename MapOut, typename Pair1>
auto conditional_push_back(MapOut&& mo, Pair1&& p1, mpl_::bool_<false>) const{
return push_back(std::forward<MapOut>(mo), std::forward<Pair1>(p1));
}
template<typename MapOut, typename Pair1>
auto conditional_push_back(MapOut&& mo, Pair1&& p1, mpl_::bool_<true>) const{
return push_back(std::forward<MapOut>(mo), make_pair<typename std::decay_t<Pair1>::first_type>(p1.second + at_key<typename std::decay_t<Pair1>::first_type>(m2_)));
}
};
template<class Map2>
merge1<Map2> make_merge1(Map2&& m2){return {std::forward<Map2>(m2)};}
template<class Map1>
struct merge2{
Map1 m1_;
template<typename MapOut, typename Pair2>
auto operator()(MapOut&& mo, Pair2&& p2) const{
return conditional_push_back(std::forward<MapOut>(mo), std::forward<Pair2>(p2), has_key<typename std::decay_t<Pair2>::first_type>(m1_));
}
template<typename MapOut, typename Pair2>
auto conditional_push_back(MapOut&& mo, Pair2&& p2, mpl_::bool_<false>) const{
return push_back(std::forward<MapOut>(mo), std::forward<Pair2>(p2));
}
template<typename MapOut, typename Pair2>
auto conditional_push_back(MapOut&& mo, Pair2&& , mpl_::bool_<true>) const{
return mo;
}
};
template<class Map1>
merge2<Map1> make_merge2(Map1&& m){return {std::forward<Map1>(m)};}
}
template<class Map1, class Map2>
inline auto accumulate_merge(Map1&& m1, Map2&& m2){
return
as_map( // not completely sure this is a good idea
fold( // second map2 is checked for unpaired elements
std::forward<Map1>(m1),
fold( // first map1 takes the lead
std::forward<Map2>(m2),
make_map<>(),
detail::make_merge1(std::forward<Map1>(m1))
),
detail::make_merge2(std::forward<Map2>(m2))
)
);
}
}}
namespace bfn = boost::fusion;
using namespace boost::fusion;
struct x{};
struct y{};
struct z{};
int main(){
auto m = make_map<x, y >(2, 4.3 );
auto n = make_map< y, z>( 2 , 8.);
auto l = accumulate_merge(m, n);
assert( at_key<x>(l) == at_key<x>(m) );
assert( at_key<y>(l) == at_key<y>(m) + at_key<y>(n) );
assert( typeid(at_key<y>(l)) == typeid(at_key<y>(m) + at_key<y>(n)) );
assert( at_key<z>(l) == at_key<z>(n) );
}
我有两个 boost::fusion::map
我想以某种方式合并。对于这两个地图,我想生成第三个地图,它具有两个地图中都存在的所有键,如果两个地图都存在,则添加值。例如:
#include <boost/fusion/container/map.hpp>
namespace bfn = boost::fusion;
using namespace boost::fusion;
struct x{};
struct y{};
struct z{};
int main(){
auto m = make_map<x, y>(2, 4.3);
auto n = make_map<x>(2.);
auto l = accumulate_merge(m, n); // how this function should look like?
}
之后 l
将等同于 make_map<x, y>(2 + 2., 4.3)
.
我不知道从哪里开始。我尝试从 join
开始(并消除重复项,但我很快就变得复杂了)。
Boost Fusion 中是否有可以帮助我的工具?
(有很多微妙之处,比如如果对于同一个键,两个相应的类型不同——但仍然可以添加——。但是任何第一个版本都会有所帮助)。
我设法用 boost::fusion::fold
两次想出了这个版本。我不确定它是否是(编译时)最优的。
基本上,我先遍历第一张地图。如果键不在第二个映射中,则将元素推送到结果。如果键在第二个映射中,则添加元素和结果(无论将什么类型推送到结果)。
最后,我在第二张地图上进行了迭代。如果密钥在第一张地图中,我将忽略它(因为它是在第一遍中添加的)。如果键不在第二个中,则将元素推送到结果中。
(结果是第一个map优先于key类型的排序)
有一些未解决的问题或疑问:
1) 有更多 efficient/compact 方法可以做到这一点
2) 我不确定 forward
的用途,基本上我到处都用它(以防万一)
3) 不确定函数 returns 中的 auto
与 decltype(auto)
。
4) 这些函数对 SFINAE 不友好,我可以添加守卫来生成软错误。 (例如,如果无法添加值)。
5) 结果没有任何自然顺序,我不知道这是算法问题还是 fold 问题,或者因为 map 没有指定 push_back
的顺序(之后都是地图)。
Comments are welcome.
现在代码:
namespace boost{
namespace fusion{
namespace detail{
template<class Map2>
struct merge1{
Map2 m2_;
template<typename MapOut, typename Pair1>
auto operator()(MapOut&& mo, Pair1&& p1) const{
return conditional_push_back(std::forward<MapOut>(mo), std::forward<Pair1>(p1), has_key<typename std::decay_t<Pair1>::first_type>(m2_));
}
template<typename MapOut, typename Pair1>
auto conditional_push_back(MapOut&& mo, Pair1&& p1, mpl_::bool_<false>) const{
return push_back(std::forward<MapOut>(mo), std::forward<Pair1>(p1));
}
template<typename MapOut, typename Pair1>
auto conditional_push_back(MapOut&& mo, Pair1&& p1, mpl_::bool_<true>) const{
return push_back(std::forward<MapOut>(mo), make_pair<typename std::decay_t<Pair1>::first_type>(p1.second + at_key<typename std::decay_t<Pair1>::first_type>(m2_)));
}
};
template<class Map2>
merge1<Map2> make_merge1(Map2&& m2){return {std::forward<Map2>(m2)};}
template<class Map1>
struct merge2{
Map1 m1_;
template<typename MapOut, typename Pair2>
auto operator()(MapOut&& mo, Pair2&& p2) const{
return conditional_push_back(std::forward<MapOut>(mo), std::forward<Pair2>(p2), has_key<typename std::decay_t<Pair2>::first_type>(m1_));
}
template<typename MapOut, typename Pair2>
auto conditional_push_back(MapOut&& mo, Pair2&& p2, mpl_::bool_<false>) const{
return push_back(std::forward<MapOut>(mo), std::forward<Pair2>(p2));
}
template<typename MapOut, typename Pair2>
auto conditional_push_back(MapOut&& mo, Pair2&& , mpl_::bool_<true>) const{
return mo;
}
};
template<class Map1>
merge2<Map1> make_merge2(Map1&& m){return {std::forward<Map1>(m)};}
}
template<class Map1, class Map2>
inline auto accumulate_merge(Map1&& m1, Map2&& m2){
return
as_map( // not completely sure this is a good idea
fold( // second map2 is checked for unpaired elements
std::forward<Map1>(m1),
fold( // first map1 takes the lead
std::forward<Map2>(m2),
make_map<>(),
detail::make_merge1(std::forward<Map1>(m1))
),
detail::make_merge2(std::forward<Map2>(m2))
)
);
}
}}
namespace bfn = boost::fusion;
using namespace boost::fusion;
struct x{};
struct y{};
struct z{};
int main(){
auto m = make_map<x, y >(2, 4.3 );
auto n = make_map< y, z>( 2 , 8.);
auto l = accumulate_merge(m, n);
assert( at_key<x>(l) == at_key<x>(m) );
assert( at_key<y>(l) == at_key<y>(m) + at_key<y>(n) );
assert( typeid(at_key<y>(l)) == typeid(at_key<y>(m) + at_key<y>(n)) );
assert( at_key<z>(l) == at_key<z>(n) );
}