c++ - boost::fusion::map 允许重复键

标签 c++ dictionary boost boost-fusion

根据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

我本来期望在 key 唯一性上有一个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;
}

最佳答案

来自docs on associative containers :

... Keys are not checked for uniqueness.

正如 Richard Hodges 所暗示的那样, 这可能是设计使然

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

关于c++ - boost::fusion::map 允许重复键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34902641/

相关文章:

c++ - C++ 中的 Typedef 函数指针

algorithm - Python:在多维字典中搜索键

php mysql 和 javascript 加载冲突

c++ - C++11 中多种数值类型的 vector

c++ - 使用 Visual Studio 2012 构建 Boost 1.52 库以面向 Windows XP

c++ - 为什么这不会编译以及如何实现才能编译?

c++ - 使用通用运算符->* 作为右值

c++ - 需要帮助使用 system.diagnostics 在 C++ 中创建秒表

python - 需要解释 Python 中 json 和 dict 的区别

c++ - boost::archive::binary_oarchive = 程序崩溃?