您能否分享Boost::MPL使用的任何真实示例(除了 lambdas),只是为了让我更好地了解它的用途和实际使用领域?MPL 文档教程有一个维度分析示例,但可能因为它是一个如此学术的示例,它并没有让我对 Boost::MPL 以及何时可以有效使用它的感觉。
8 回答
我使用 Boost.Mpl 来生成类似变体的类。
例如,给定一个 MPL 类型列表,如下所示:
typedef boost::mpl::set<Foo, Bar, Baz> type_set;
然后,我使用boost::mpl::fold
构建一个从彼此派生的类链,每个类都添加std::unordered_set
类型集中的一种类型。最终结果是一个包含 an unordered_set<Foo>
、 anunordered_set<Bar>
和 an 的类unordered_set<Baz>
。
而且因为类是根据 a 指定的boost::mpl::set
,所以我可以遍历这些类型以自动生成其他函数,例如operator==
比较所有unordered_set
s 的 a。
事实上,Boost.MPL 和 Boost.Preprocessor 一样,都是真正的构建块。
大多数时候,您可能会通过其他库使用它,因为许多 Boost 库都是基于这两个库构建的。
例如:
- Boost.Fusion(跨越编译时和运行时领域之间的差距)
- Boost.MultiIndex(为了更简单的界面)
- Boost.Unit(用于维度分析)
- 我认为 Boost.Variant 可能也取决于它
您可能已经在不知不觉中使用它:)
我使用了一个更强大的维度分析库,称为 Boost.Units。
我开发了一个编译时反射库,然后使用该库构建了一个通用类,该类为传入的任何编译时反射类型提供运行时反射。我使用该支持自动生成 UI 组件以编辑属性这种反映类型。
它对于我们应用程序中的事件分布也很重要。例如,当有人更改他们希望系统所在的单位时,我不必告诉系统新项目已添加到给定设备,因为代码使用 MPL 来分析这些类型并且只知道添加了一些东西并改变它。
我刚刚使用元编程技术将 Qt 信号包装成某种东西,以重新获得系统移除的类型安全性,并能够与任何功能实体连接。
但说实话,当您使用诸如排序之类的标准算法时,您几乎肯定已经使用了实际应用的元编程技术。排序算法的一个体面实现使用一种进化程度较低的元编程形式来分析传入的迭代器,然后使用标签调度来启动能够充分利用这些迭代器特性的排序算法。
坦率地说,如果您不进行元编程,那么您就没有利用 C++ 的强大功能,您还不如使用其他东西。
在构建撮合引擎时,主要针对交易区的交易所或暗池,我们通常需要检查两个订单是否匹配(或者我们说是否可以交叉),可能有很多方面需要检查,我们称之为规则,以下是组织这些规则的关键要求:
- 添加新规则并得到应用应该很容易
- 将规则组织成不同的组以应用检查应该很方便
- 因为规则检查被非常频繁地调用 - 当然,这是匹配引擎的唯一工作,我们希望它尽可能优化
这非常适合使用 boost mpl,它可以使用编译时间序列boost::mpl::vector
来组织规则,并将它们应用到boost::mpl::for_each
.
这个想法最好用一个例子来说明:
- 通过简单地定义一个新的 Rule 类来添加一个新的规则是直接的
- 使用 可以方便地对规则进行分组
boost::mpl::vector
,并将其用作模板参数进行canCross
检查 - 因为大部分设置工作都是在编译时完成的,所以速度很快。
#include <iostream>
#include <vector>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
using namespace std;
struct Order {};
struct Rule1
{
const char* name() const { return "Rule1"; }
bool apply(const Order& a, const Order& b) const { cout << "Checking Rule1..." << endl; return true; }
};
struct Rule2
{
const char* name() const { return "Rule2"; }
bool apply(const Order& a, const Order& b) const { cout << "Checking Rule2..." << endl; return false;}
};
struct Rule3
{
const char* name() const { return "Rule3"; }
bool apply(const Order& a, const Order& b) const { cout << "Checking Rule3..." << endl; return false;}
};
struct Rule4
{
const char* name() const { return "Rule4"; }
bool apply(const Order& a, const Order& b) const { cout << "Checking Rule4..." << endl; return true;}
};
struct RuleApplicator
{
RuleApplicator(bool& success, std::vector<const char*>& failedRules, const Order& order1, const Order& order2):
_success(success),
_failedRules(failedRules),
_order1(order1),
_order2(order2)
{}
template <typename U> void operator() (U rule)
{
if(!rule.apply(_order1, _order2))
{
_success = false;
_failedRules.push_back(rule.name());
}
}
private:
bool& _success;
std::vector<const char*>& _failedRules;
const Order& _order1;
const Order& _order2;
};
template <class Rules>
bool canCross(const Order& a, const Order& b)
{
bool success = true;
std::vector<const char*> failedRules;
RuleApplicator applicator(success, failedRules, a, b);
boost::mpl::for_each<Rules>(applicator);
if (!success)
{
cout << "Can't cross due to rule check failure:";
for(const char* ruleName: failedRules)
{
cout << ruleName << " ";
}
cout << endl;
return false;
}
else
{
cout << "Can cross!" << endl;
return true;
}
}
int main(int argc, char** argv)
{
Order a, b;
canCross<boost::mpl::vector<Rule1, Rule4>>(a, b);
cout << endl;
canCross<boost::mpl::vector<Rule1, Rule2, Rule3, Rule4>>(a, b);
}
您将看到输出为:
Checking Rule1...
Checking Rule4...
Can cross!
Checking Rule1...
Checking Rule2...
Checking Rule3...
Checking Rule4...
Can't cross due to rule check failure:Rule2 Rule3
如果您的应用程序在处理键值对时具有繁重的逻辑,则您将需要一种超有效的方法来从键中获取值,典型的 hashmap 效果很好,但是如果预先知道可能的键,则可以使用 boost::mpl 进行优化,使用数组,以及在编译时将键转换为数组索引的方法,这当然更有效。
这是一个处理fix message的例子,它是一个包含各种键值对的消息,它在金融交易应用程序中被大量使用:
#include <iostream>
#include <array>
#include <string>
#include <unordered_set>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/size.hpp>
using namespace std;
using namespace boost;
struct TagEntity
{
bool isValid;
std::string value;
};
template<class CommonTags>
struct FixMsg
{
static constexpr uint32_t CommonTagsCount = mpl::size<CommonTags>::type::value;
template<int Tag>
constexpr uint32_t index()
{
constexpr auto idx = mpl::find<CommonTags, mpl::integral_c<int, Tag>>::type::pos::value; // this is the key step: convert tag to index in compile time
static_assert(idx < CommonTagsCount, "tag not found");
return idx;
}
template<int Tag>
TagEntity& getTagEntity()
{
return _commonTags[index<Tag>()];
}
std::array<TagEntity, CommonTagsCount> _commonTags; // or else use std::unordered_set, which is not as fast as this approach: absolute O(1) in runtime
};
int main(int argc, char** argv)
{
using MyCommonTags = mpl::vector_c<int,
11,
35,
10914,
10916>;
FixMsg<MyCommonTags> fixMsg;
auto& tagEntity = fixMsg.getTagEntity<11>();
tagEntity.isValid = true;
tagEntity.value = "Get tag entity in O(1)";
cout << tagEntity.value << endl;
我在我的stat_log库中广泛使用 boost::mpl(和 boost::fusion)。该库允许用户指定统计和日志标签的层次结构及其相关行为,即每个标签的统计类型(直方图、计数器等)。
我严重依赖元编程来对用户做正确的事情:
stat_log::writeStat<IP_PKTS_RCVD>(450);
例如,如果用户定义了类型特征:
template <>
struct stat_tag_to_type<IP_PKTS_RCVD>
{
using type = Accumulator<
stat_log::HistogramCount<
int,
1, //start bin
1500, //stop bin
10 //num_bits
>
>;
};
上面的“writeStat”调用将代理(在编译时)直方图统计。这种设计技术的强大之处在于“writeStat”调用站点根本不与所选的特定统计信息耦合。
我还使用大量的 MPL 和 boost::fusion 来实际查看统计数据。根据您的问题,请参阅以下文件以了解 boost::mpl 的最高浓度:
https://github.com/rjmccabe3701/stat_log/blob/master/include/stat_log/util/stat_log_impl.h https://github.com/rjmccabe3701/stat_log/blob/master/include/stat_log/util/tag_commander.h https://github.com/rjmccabe3701/stat_log/blob/master/include/stat_log/stat_log.h
尤其是 stat_log_impl.h 中漂亮的模板元“函数”:
//This template is used in conjunction with an MPL algorithm
// with the same semantics as mpl::find_if.
//BoolFunc is the "condition" metafunction.
//StatTagFunc is a metafunction that transforms the given
// stat_tag into something the algorithm requires.
// For example the "Identity" metafunction would work here.
//StatTagArgs is extra arguments to the BoolFunc
template <template<typename...> class BoolFunc,
template<typename...> class StatTagFunc,
class... StatTagArgs>
struct tag_node_query
{
template<typename TheTagNode>
struct apply
{
using stat_tag = typename TheTagNode::tag;
using type = std::integral_constant
<
bool,
BoolFunc<
typename StatTagFunc<stat_tag>::type,
StatTagArgs...
>::value
>;
};
};
为了补充 Matthieu 的答案,它在Boost.Python和Luabind中也得到了广泛使用。
我做了一些有趣的事情: https ://github.com/edubois/static-factorial/blob/master/main.cpp
它使用 boost::mpl 的一小部分来静态计算 factorial<8>() 的值...
这有助于理解主要思想。