Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Произвольный порядок списка инициализации образца

Anna | 24.06.2014 | нет комментариев

Думаю многие кто работает с образцами, знакомы со дальнейшей обстановкой. У нас есть некоторый шаблонный класс с кучей шаблонных параметров

struct deferred;
struct deadline;
struct disable;

template<class T, class Deferred = disable, class Deadline = disable>
struct some_container

Пускай это будет некоторый интерфейс отложенного выполнения задач (энергичная очередь). И мы хотим расширить её функционал добавив отложенное выполнение и дедлайны. Но не хотим что бы все это было сразу включено, а хотим что бы дозволено было собирать необходимую конфигурацию под свои нужды. Задача таких образцов (у которых много параметров и примерно все теснее имею значение по умолчанию) в том, что для того что бы переопределить, скажем, конечный параметр нам необходимо указать все перед ним стоящие.

typedef some_container<int, disable, deadline> deadline_container;
<source>
А хотелось бы 

<source lang=cpp>
typedef some_container<int, deadline> deadline_container;

А ещё отличнее, что бы даже порядок задания не имел значение и следующие два, были бы равнозначны

typedef some_container<int, deferred, deadline> full_container1;
typedef some_container<int, deadline, deferred> full_container1;

Но мы восхитительно понимаем, что как только мы поменяли два параметр у нас получится абсолютно не то чего мы ждали (это вам не tuple где порядок указание не имеет значении)
Думаю многие теснее подумали о том, что каждого этого дозволено добиться добавив прослойку между нашим типом и пользователем, для которой написать все допустимые специализации. Если у вас только 2 шаблонных параметр то да, если 3 то теснее трудно, а если добавится 4, 5 то пиши пропало. Да и как правило добавление нового параметра приводит к переделыванию всех предыдущих специализаций (так как в специализации мы не можем увеличивать число шаблонных параметров, а можем их только сокращать).
Если вас заинтересовало, умоляю под кат, я покажу вам как добиться этого

Но для начала немножко дёгтя. Шаблонные типы отличны тем, что пользователь может параметризовать их различными типами, в том числе и своими. Метод тот, что я хочу показать не разрешает специализировать образец произвольным типом. Т.е. скажем вы определи свой тип deadline_super тот, что сопоставим с типом deadline, тогда вы можете подставлять его в специализацию образца

typedef some_container<int, disable, deadline_super>

Но вы не сумеете применять данный тип в том механизме тот, что дозволят специализировать образец в произвольном виде. Вероятно это исключительное серьёзное лимитация. Рассматривая данный аспект становится ясно, что данный механизм комфортно применять при написании модульных компонент, а не расширяемых либо полиморфных.
Каждая реализация базируется на такой компоненте boost как mpl::set. Я не буду много рассказывать о том, что такое boost::mpl, скажу лишь что mpl::set разрешает создавать подобный std::set контейнер но на этапе компиляции и состоящий из типов.
Первое что нам понадобится это метод проверки списка типов на присутствие надобного нам типа, и выдачу некоторого дефолтного значения (struct disable) в отвратном случае

template <class Plugin,  class IsSet>
struct get_plugin_impl;

template <class Plugin>
struct get_plugin_impl<Plugin, boost::mpl::false_>
{
    typedef disable_plugin type;
};

template <class Plugin>
struct get_plugin_impl<Plugin, boost::mpl::true_>
{
    typedef Plugin type;
};

template <class List, class Plugin>
struct get_plugin
{
    typedef typename get_plugin_impl<Plugin,
        typename boost::mpl::has_key<List, Plugin>::type>::type type;
};

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

template <class List>
struct get_plugins
{
    typedef typename get_plugin<List, deferred_plugin>::type deferred;
    typedef typename get_plugin<List, deadline_plugin>::type deadline;
};

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

template<class T, 
         class P1 = disable_plugin,
         class P2 = disable_plugin>
struct container
{
    typedef boost::mpl::set<P1, P2> plugin_list;
    typedef get_plugins<plugin_list> plugs;

    typedef typename plugs::deferred deferred;
    typedef typename plugs::deadline deadline;

    typedef some_container<T, deferred, deadline> type; 
};

Именно она разрешает нам отвлекаться от числа и порядка указания шаблонных параметров. По сути она объединяет в себе все те (2^n K) специализаций тот, что нам пришлось бы писать для разного числа заданных шаблонных параметров и их порядка.

Возвращаясь нашему шаблонному типу, я покажу вам как это работает

#define static_type(name) 
    static const std::string& type() 
    { 
        static std::string type_(name); 
        return type_; 
    } 

struct deferred_plugin
{ static_type("deferred"); };

struct deadline_plugin
{ static_type("deadline"); };

struct disable_plugin
{ static_type("disable"); };

template<class T, class Deferred, class Deadline>
struct some_container
{
    static const std::string& type()
    {
        static std::string type_("some_container<"   
                                    Deferred::type()   ", "  
                                    Deadline::type()   ">");
        return type_;
    }
};

Применение

   cout << container<int>::type::type() << std::endl;
   cout << container<int, deadline_plugin>::type::type() << std::endl;
   cout << container<int, deferred_plugin>::type::type() << std::endl;
   cout << container<int, deferred_plugin, deadline_plugin>::type::type() << std::endl;
   cout << container<int, deadline_plugin, deferred_plugin>::type::type() << std::endl;

Выхлоп

some_container<disable, disable>
some_container<disable, deadline>
some_container<deferred, disable>
some_container<deferred, deadline>
some_container<deferred, deadline>

Обратите внимание, что порядок специализации сохранен в надобном виде, не зависимо от того в каком порядке были указаны типы при инстанцировании образца.

Вот и все, верю кому то это поможет сделать код больше прекрасным и опрятным.
Исходникик: git

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB