Примеры практического использования Boost :: MPL? - PullRequest
25 голосов
/ 09 января 2011

Можете ли вы поделиться какими-либо реальными примерами использования Boost :: MPL (кроме лямбд), только чтобы я лучше понял его цели и область практического использования? В учебном пособии по MPL есть пример размерного анализа , но, возможно, из-за того, что это такой академический пример, он не дал мне ощущения Boost :: MPL и того, когда его можно эффективно использовать.

Ответы [ 8 ]

13 голосов
/ 09 января 2011

Дело в том, что Boost.MPL, как и Boost.Preprocessor, действительно являются строительными блоками.

В большинстве случаев вы, вероятно, используете его через другие библиотеки, так как на них основано множество библиотек Boost.

Например:

  • Boost.Fusion (который пересекает промежутки между областями времени компиляции и выполнения)
  • Boost.MultiIndex (для более простого интерфейса)
  • Boost.Unit (для размерного анализа)
  • Boost. Вариант, я думаю, тоже зависит от него

Вы можете использовать его уже неизвестно:)

11 голосов
/ 09 января 2011

Я использовал 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, я могу выполнить итерации по этим типам дляавтоматически генерировать и другие функции, такие как operator==, который сравнивает все unordered_set s.

4 голосов
/ 09 января 2011

Я использую более расширенную библиотеку анализа измерений под названием Boost.Units.

Я разработал библиотеку отражений во время компиляции, а затем использовал эту библиотеку для создания универсального класса, который обеспечивает отражение во время выполнения для любого переданного отраженного типа во время компиляции. Я использовал эту поддержку для автоматической генерации компонентов пользовательского интерфейса для редактировать свойства таких отраженных типов.

Это также имеет первостепенное значение для распределения событий в нашем приложении. Например, когда кто-то меняет единицы, в которых они хотят, чтобы система была включена, мне не нужно учить эту систему, что новые элементы были добавлены к данным устройствам, потому что код использует MPL для анализа этих типов и просто знает, что что-то было добавлено и меняет его.

Я только что использовал методы метапрограммирования, чтобы обернуть сигналы Qt во что-то, что восстанавливает безопасность типов, удаленную их системой, и способно соединяться с любым функциональным объектом.

Но, по правде говоря, вы почти наверняка уже использовали практически применяемые методы метапрограммирования, когда использовали стандартные алгоритмы, такие как sort. Достойная реализация алгоритма сортировки использует менее развитую форму метапрограммирования для анализа переданных итераторов, а затем использует диспетчеризацию тегов для запуска алгоритма сортировки, способного полностью использовать возможности этих итераторов.

Честно говоря, если вы не занимаетесь метапрограммированием, то вы не используете возможности C ++ и можете использовать что-то еще.

3 голосов
/ 19 сентября 2015

Я широко использую boost :: mpl (и boost :: fusion) в моей stat_log библиотеке.Эта библиотека позволяет пользователю задавать иерархию статистических и регистрирующих тегов и связанных с ними поведений, то есть типов статистики для каждого тега (гистограмма, счетчик и т. Д.).

Я в значительной степени полагаюсь на метапрограммирование, чтобы делать правильные вещи спользователь делает:

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
         >;
   };
};
3 голосов
/ 12 марта 2011

Чтобы добавить ответ Матье, он также довольно широко используется в Boost.Python и Luabind .

2 голосов
/ 24 марта 2019

Если ваше приложение имеет сложную логику в работе с парами ключ-значение, вам понадобится сверхэффективный способ получить значение из ключа, типичная хэш-карта работает хорошо, но если возможные ключи известны заранее, оптимизация может быть выполнена с использованием boost: : mpl, с массивом и методом для преобразования вашего ключа в индекс массива во время компиляции, это, безусловно, более эффективно.

Вот пример обработки исправления сообщения , которое представляет собой сообщение, содержащее различные пары ключ-значение, и широко используется в приложениях для финансовой торговли:

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

2 голосов
/ 24 марта 2019

Когда речь идет о создании соответствующего движка, в основном для Exchange или DarkPool в торговой зоне, нам обычно нужно проверять, могут ли 2 ордера совпадать или нет (или мы говорим, могут пересекаться или нет), может быть много аспектов, чтобы проверить, какиемы называем Правила , и вот основные требования к организации этих правил:

  • Должно быть легко добавить новое правило и получить применение
  • Должно быть удобно организовывать правила в разные группы для применения проверок
  • Поскольку проверка правил вызывается довольно часто - ну, конечно, это единственная работа соответствующего механизма, мы хотим, чтобы он был оптимальнымнасколько это возможно

Это прекрасно подходит для использования boost mpl, который может использовать последовательность времени компиляции boost::mpl::vector для организации правил и применять их с помощью boost::mpl::for_each.

.Идея лучше всего проиллюстрирована на примере:

  • Добавить новое правило просто, просто определив новый класс правил
  • Удобно группироватьЕсли вы используете boost::mpl::vector и используете его в качестве параметра шаблона для canCross check
  • Поскольку большая часть работы по настройке выполняется во время компиляции, это быстро.
#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
2 голосов
/ 20 января 2015

Что-то смешное, что я сделал: https://github.com/edubois/static-factorial/blob/master/main.cpp

Он использует небольшую часть boost :: mpl для статического вычисления значения факториала <8> () ...

Это можетпомогите понять основную идею.

...