2016-01-20 3 views
2

По boost::fusion::map документы:повышение :: фьюжн :: Карта позволяет дубликаты ключей

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)

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

namespace fusion = ::boost::fusion; 

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

int main() 
{ 
    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(m, Foo {}); 
    return 0; 
} 

Благодаря комментариям ниже, вот некоторые дополнительные подробности о том, что я на самом деле пытаетесь достичь.

Идея состоит в том, чтобы автоматически генерировать код сериализации 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 вы можете предложить, что использовать вместо 'fusion', если он действительно устарел? –

ответ

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 уже дала ответ), но ответа на запрос к уточните некоторые мои комментарии.

Одним из способов достижения ключевой уникальности было бы использование сырья mpl. Например, принимая сообщение 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, или вы можете deserialze любое сообщение и получить строго типизированное FixMessage.

Я не уверен, если вы собираетесь получить ошибку компиляции, если вы используете тот же самый тип дважды, но я уверен, что вы увидите только один раз при итерации.