boost::fusion::map 允许重复键
boost::fusion::map allows duplicate keys
根据 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;
}
来自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,
^
这不是一个答案(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
轻松序列化它,或者您可以反序列化任何消息并获得 strongly-typed FixMessage。
我不确定如果两次使用相同的类型是否会出现编译错误,但我确定您在迭代时只会看到一次类型。
根据 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;
}
来自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,
^
这不是一个答案(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
轻松序列化它,或者您可以反序列化任何消息并获得 strongly-typed FixMessage。
我不确定如果两次使用相同的类型是否会出现编译错误,但我确定您在迭代时只会看到一次类型。