2016-01-20 28 views
2

按照boost::fusion::map文档:的boost ::融合::地图允许重复键

A map may contain at most one element for each key.

在实践中,很容易违反此。

我能够定义以下类型:

using map_type = fusion::map< 
    fusion::pair<int, char> 
    , fusion::pair<int, char> 
    , fusion::pair<int, char>>; 

,并用这些重复的键实例是:

map_type m(
    fusion::make_pair<int>('X') 
    , fusion::make_pair<int>('Y') 
    , fusion::make_pair<int>('Z')); 

迭代使用fusion::for_each在地图上显示的数据结构确实含有3双,并且每个键都是int类型:

struct Foo 
{ 
    template<typename Pair> 
    void operator()(const Pair& p) const 
    { 
     std::cout << typeid(typename Pair::first_type).name() << "=" << p.second << '\n'; 
    } 
}; 
fusion::for_each(m, Foo {}); 

输出:

i=X 
i=Y 
i=Z 

我本来期望一个static_assert上键唯一,但这显然并非如此。

  • 这是为什么?

  • 如何确保没有人可以实例化带有重复键的fusion::map

全部工作示例:on coliru

​​

由于下面的评论,这里有什么我确实想实现一些进一步的细节。

这个想法是自动生成FIX序列化代码。

一个给定的字段类型只能在任何给定的FIX消息存在一次 - 因此想要的static_assert

激励例如:on coliru

#include <boost/fusion/container.hpp> 
#include <boost/fusion/sequence.hpp> 
#include <boost/fusion/include/for_each.hpp> 
#include <boost/mpl/transform.hpp> 
#include <iostream> 

namespace fusion = ::boost::fusion; 
namespace mpl = ::boost::mpl; 

template<class Field> 
struct MakePair 
{ 
    using type = typename fusion::result_of::make_pair<Field, typename Field::Type>::type; 
}; 

template<class Fields> 
struct Map 
{ 
    using pair_sequence = typename mpl::transform<Fields, MakePair<mpl::_1>>::type; 
    using type   = typename fusion::result_of::as_map<pair_sequence>::type; 
}; 

/////////////////////////// 

template<typename... Fields> 
class Message 
{ 
public: 
    template<class Field> 
    void set(const typename Field::Type& val) 
    { 
     fusion::at_key<Field>(_fields) = val; 
    } 

    void serialise() 
    { 
     fusion::for_each(_fields, Serialiser {}); 
    } 
private: 

    struct Serialiser 
    { 
     template<typename Pair> 
     void operator()(const Pair& pair) const 
     { 
      using Field = typename Pair::first_type; 

      std::cout << Field::Tag << "=" << pair.second << "|"; 
     } 
    }; 

    using FieldsVector = fusion::vector<Fields...>; 
    using FieldsMap = typename Map<FieldsVector>::type; 

    FieldsMap _fields; 

    static_assert(fusion::result_of::size<FieldsMap>::value == fusion::result_of::size<FieldsVector>::value, 
      "message must be constructed from unique types"); // this assertion doesn't work 
}; 

/////////////////////////// 

#define MSG_FIELD(NAME, TYPE, TAG) \ 
    struct NAME      \ 
    {        \ 
     using Type = TYPE;   \ 
     static const int Tag = TAG; \ 
    }; 

MSG_FIELD(MsgType, char, 35) 
MSG_FIELD(Qty,  int, 14) 
MSG_FIELD(Price, double, 44) 

using Quote = Message<MsgType, Qty, Price>; 

/////////////////////////// 

int main() 
{ 
    Quote q; 
    q.set<MsgType>('a'); 
    q.set<Qty>(5); 
    q.set<Price>(1.23); 

    q.serialise(); 
    return 0; 
} 
+0

发送问题boost :: fusion authors :)无论如何,我确实认为boost :: fusion已经过时了。 – SergeyA

+1

不会static_assert涉及每次遇到几何模板扩展? –

+0

@SergeyA如果确实已经过时了,你可以建议使用什么来代替“融合”吗? –

回答

2

docs on associative containers

... Keys are not checked for uniqueness.

正如所提及的Richard Hodges,th是有可能通过设计

wouldn't that static_assert involve a geometric template expansion each time it was encountered?

尽管如此,可以使用boost::mpl以减少提供给fusion::map成一个独特的序列的序列,和static_assert在序列长度是相同的。

首先,我们创建它迭代类型列表中,并创建独特类型

// given a sequence, returns a new sequence with no duplicates 
// equivalent to: 
// vector UniqueSeq(vector Seq) 
//  vector newSeq = {} 
//  set uniqueElems = {} 
//  for (elem : Seq) 
//   if (!uniqueElems.find(elem)) 
//    newSeq += elem 
//    uniqueElems += elem 
//  return newSeq 
template<class Seq> 
struct UniqueSeq 
{ 
    using type = typename mpl::accumulate< 
     Seq, 
     mpl::pair<typename mpl::clear<Seq>::type, mpl::set0<> >, 
     mpl::if_< 
      mpl::contains<mpl::second<mpl::_1>, mpl::_2>, 
      mpl::_1, 
      mpl::pair< 
       mpl::push_back<mpl::first<mpl::_1>, mpl::_2>, 
       mpl::insert<mpl::second<mpl::_1>, mpl::_2> 
      > 
     > 
    >::type::first; 
}; 

然后我们改变Map定义使用UniqueSeq::type生成的序列结构pair_sequence

// given a sequence of fields, returns a fusion map which maps (Field -> Field's associate type) 
template<class Fields> 
struct Map 
{ 
    using unique_fields = typename UniqueSeq<Fields>::type; 
    using pair_sequence = typename mpl::transform<unique_fields, MakePair<mpl::_1>>::type; 
    using type   = typename fusion::result_of::as_map<pair_sequence>::type; 
}; 

因此给定一个字段列表,我们可以创建一个fusion::vector和一个fusion::map,结果为UniqueSeq<Fields>,并断言每个字段的大小相同:

using FieldsVector = fusion::vector<Fields...>; 
using FieldsMap = typename Map<FieldsVector>::type; 

static_assert(fusion::result_of::size<FieldsMap>::value == fusion::result_of::size<FieldsVector>::value, 
     "message must be constructed from unique types"); 

现在转到重复场导致编译错误:

static assertion failed: message must be constructed from unique types

scratch/main.cpp: In instantiation of ‘class Message<Qty, Price, Qty>’: 
scratch/main.cpp:129:23: required from here 
scratch/main.cpp:96:5: error: static assertion failed: message must be constructed from unique types 
    static_assert(fusion::result_of::size<FieldsMap>::value == fusion::result_of::size<FieldsVector>::value, 
    ^

Full example on coliru

1

它不是一个答案(OP已经给出了一个答案),但请求的响应澄清我的一些评论。

实现密钥唯一性的一种方法是thrugh raw mpl usage。例如,以FIX消息为我们的域名,下面的一段代码应该说明这个想法。该代码未被编译并仅作为通用插图示例提供。

template <class ValueType, int FieldTag> 
struct FixField { 
    using value_t = ValueType; 
    static const short tag = FieldTag; 
}; 

using CumQty = FixField<double, 14>; 
using Price = FixField<double, 44>; 

using inherit = boost::mpl::inherit<boost::mpl::placeholders::_1, boost::mpl::placeholders::_2>; 

template <class list> 
using inherit_linearly = boost::mpl::inherit_linearly<list, inherit>::type; 

template <class Members> 
struct FixMessage : iherit_linearly<Members> { 
    using members_t = Members; 
    template <class T> T& get() { return static_cast<T&>(*this); } // const ver as well 
}; 
struct ExecutionReport : public FixMessage<boost::mpl::set<CumQty, Price> > { 
    static constexpr char const* name = "ExecutionReport"; 
}; 

现在你对所要执行的报告有所不满。您可以使用boost::mpl::for_each轻松将其序列化,或者可以反序列化任何消息并获取强类型的FixMessage。

我不确定如果你两次使用相同的类型会得到编译错误,但是我相信你只会在迭代时看到一次类型。