Ejemplos de uso práctico de Boost :: MPL?

Can you share any real-world examples of Boost::MPL usage (except lambdas), just to let me better understand its purposes and field of practical usage? The MPL documentation tutorial has a análisis dimensional example, but maybe because it's such an academic example it hasn't given me a feeling of Boost::MPL and when it can be effectively used.

preguntado el 09 de enero de 11 a las 11:01

8 Respuestas

I've used Boost.Mpl to generate variant-like classes.

For example, given a MPL type list such as this:

typedef boost::mpl::set<Foo, Bar, Baz> type_set;

Luego uso boost::mpl::fold to build a chain of classes derived from each others which each adds an std::unordered_set of one of the types in the type set. The end result is a class which contains an unordered_set<Foo>, un unordered_set<Bar> y un unordered_set<Baz>.

And because the class is specified in terms of a boost::mpl::set, I can iterate over these types to automatically generate other functions as well, such as an operator== which compares all of the unordered_sets.

Respondido el 09 de enero de 11 a las 17:01

The fact is, Boost.MPL, like Boost.Preprocessor, are really building blocks.

Most of the times, you probably use it through other libraries, as a number of Boost libraries are built upon those two.

Por ejemplo:

  • Boost.Fusion (which crosses the gaps between compile-time and run-time realms)
  • Boost.MultiIndex (for an easier interface)
  • Boost.Unit (for dimensional analysis)
  • Boost.Variant may, I think, also depends on it

You may use it unknowningly already :)

Respondido el 09 de enero de 11 a las 18:01

I use a more enhanced dimensional analysis library called Boost.Units.

I've developed a compile-time reflection library and then used that library to build a generic class that provides runtime-reflection to any compile-time reflected type passed in. I've used that support to automatically generate UI components to edit the properties of such reflected types.

It's also paramount to the distribution of events within our application. For instance, when someone changes the units they wish the system to be in, I don't have to teach that system that new items have been added to given devices because the code uses MPL to analyze those types and just knows that something's been added and changes it.

I've just used metaprogramming techniques to wrap up the Qt signals into something that regains the type safety removed by their system and is able to connect with any functional entity.

But to tell the truth, you've almost certainly used practically applied metaprogramming techniques already when you've used standard algorithms like sort. A decent implementation of the sort algorithm uses a less evolved form of metaprogramming to analyze the iterators passed in and then uses tag-dispatching to initiate a sort algorithm capable of fully utilizing the features of those iterators.

Quite frankly, if you're not doing metaprogramming then you're not utilizing the power of C++ and you may as well be using something else.

Respondido el 09 de enero de 11 a las 14:01

I think the question is about Boost.MPL and not metaprogramming in general. - jalf

You can't talk about MPL without all the stuff that lead up to it. - Edward extraño

but you can ask "do you use Boost.MPL" without asking "do you use any other example of template metaprogramming", in the same way that you can ask "do you drive a Volvo" without asking "Do you drive a car" - jalf

When it comes to build matching engine, mostly for Exchange or DarkPool in trading area, we usually need to check if 2 orders could match or not (or we say could cross or not), there could be many aspects to check which we call as Reglas, and here are the key requirements in term of organizing these rules:

  • It should be easy to add a new rule and get applied
  • It should be convenient to organize rules into different groups to apply the checks
  • Because the rule check are invoked quite frequently - well, of couse, that is the sole job of a matching engine, we want it as optimal as possible

This is a great fit to use boost mpl, which could use compile time sequence boost::mpl::vector to organize rules, and get them applied with boost::mpl::for_each.

The idea is best illustrated with an example:

  • It is straightforwrad to add a new rule by simply defining a new Rule class
  • It is convenient to group rules using boost::mpl::vector, and use it as a template parameter for canCross comprobar
  • Because most of the setting up work are done in compile time, it is fast.
#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);
}


You will see output as:

Checking Rule1...
Checking Rule4...
Can cross!

Checking Rule1...
Checking Rule2...
Checking Rule3...
Checking Rule4...
Can't cross due to rule check failure:Rule2 Rule3

respondido 24 mar '19, 05:03

If your application has heavy logic in dealing with key-value pairs, you will need a ultra efficent way to get value from key, typical hashmap works well, but if possible keys are known upfront, optimzation could be done use boost::mpl, with an array, and a method to convert your key to array index at compile time, this is certainly more efficent.

Here is an example on handling fix message, which is a message contains various key-value pairs, it is used heavily in financial trading applications:

#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;

respondido 24 mar '19, 07:03

I use boost::mpl (and boost::fusion) extensively in my stat_log library. This library allows the user to specify a hierarchy of statistic and logging tags and their associated behaviors, i.e. per-tag statistic types (histogram, counter, etc).

I rely heavily on metaprogramming to do the right thing with the user does:

stat_log::writeStat<IP_PKTS_RCVD>(450);

For example if the user defines the type trait:

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
        >
     >;
};

the "writeStat" call above will proxy (at compile time) to a histogram statistic. The powerful aspect of this design technique is the "writeStat" call site is not at all coupled with the particular statistic chosen.

I also use a wealth of MPL and boost::fusion to actually view the stats. Per your question, see the following files for the highest concentration of 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

especially the nifty template meta "function" in 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
         >;
   };
};

Respondido el 20 de Septiembre de 15 a las 18:09

Broken links and deception :( - Quentin

To add to Matthieu's answer, it's also used quite extensively throughout both Boost.Python y Luabind.

respondido 12 mar '11, 22:03

boost.proto, boost.spirit (qi,karma,lex)... actually in most other boost libraries... - user2346536

Something funny I did: https://github.com/edubois/static-factorial/blob/master/main.cpp

It uses a tiny part of boost::mpl to statically compute the value of factorial<8>()...

This can help to understand the main idea.

Respondido el 20 de enero de 15 a las 00:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.