【发布时间】:2011-06-06 01:16:16
【问题描述】:
您能否分享任何Boost::MPL 用法的真实示例(lambda 除外),只是为了让我更好地了解它的用途和实际使用领域? MPL 文档教程中有一个dimensional analysis 示例,但可能因为它是一个学术示例,它并没有让我感觉 Boost::MPL 以及何时可以有效使用。
【问题讨论】:
您能否分享任何Boost::MPL 用法的真实示例(lambda 除外),只是为了让我更好地了解它的用途和实际使用领域? MPL 文档教程中有一个dimensional analysis 示例,但可能因为它是一个学术示例,它并没有让我感觉 Boost::MPL 以及何时可以有效使用。
【问题讨论】:
我使用 Boost.Mpl 来生成类似变体的类。
例如,给定一个 MPL 类型列表,如下所示:
typedef boost::mpl::set<Foo, Bar, Baz> type_set;
然后我使用boost::mpl::fold 构建一个从彼此派生的类链,每个类都添加一个类型集中其中一种类型的std::unordered_set。最终结果是一个包含unordered_set<Foo>、unordered_set<Bar> 和unordered_set<Baz> 的类。
由于该类是根据boost::mpl::set 指定的,因此我可以遍历这些类型以自动生成其他函数,例如比较所有unordered_sets 的operator==。
【讨论】:
事实上,Boost.MPL 和 Boost.Preprocessor 一样,都是真正的构建块。
大多数时候,您可能会通过其他库使用它,因为许多 Boost 库都是基于这两个库构建的。
例如:
你可能已经在不知不觉中使用它了:)
【讨论】:
我使用了一个更强大的维度分析库,称为 Boost.Units。
我开发了一个编译时反射库,然后使用该库构建了一个通用类,该类为传入的任何编译时反射类型提供运行时反射。我使用该支持自动生成 UI 组件以编辑此类反射类型的属性。
这对于在我们的应用程序中分发事件也至关重要。例如,当有人更改他们希望系统所在的单位时,我不必告诉系统新项目已添加到给定设备,因为代码使用 MPL 来分析这些类型并且只知道添加了一些东西并改变它。
我刚刚使用元编程技术将 Qt 信号封装成某种东西,以重新获得系统移除的类型安全性,并且能够与任何功能实体连接。
但说实话,当您使用诸如排序之类的标准算法时,几乎可以肯定您已经使用了实际应用的元编程技术。排序算法的一个不错的实现是使用进化程度较低的元编程形式来分析传入的迭代器,然后使用标签调度来启动能够充分利用这些迭代器特性的排序算法。
坦率地说,如果您不进行元编程,那么您就没有利用 C++ 的强大功能,您还不如使用其他东西。
【讨论】:
在构建撮合引擎时,主要是针对交易区的交易所或暗池,我们通常需要检查两个订单是否可以匹配(或者我们说可以交叉或不交叉),可能有很多方面要检查哪个我们称之为规则,以下是组织这些规则的关键要求:
这非常适合使用boost mpl,它可以使用编译时间序列boost::mpl::vector来组织规则,并通过boost::mpl::for_each来应用它们。
这个想法最好用一个例子来说明:
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 的一小部分来静态计算阶乘的值()...
这有助于理解主要思想。
【讨论】: