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

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

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

typedef some_container deadline_container; А ещё лучше, что бы даже порядок задания не имел значение и следующие два, были бы эквивалентны typedef some_container full_container1; typedef some_container full_container1; Но мы прекрасно понимаем, что как только мы поменяли два параметр у нас получится совершенно не то чего мы ожидали (это вам не tuple где порядок указание не имеет значении)Думаю многие уже подумали о том, что всего этого можно добиться добавив прослойку между нашим типом и пользователем, для которой написать все возможные специализации. Если у вас только 2 шаблонных параметр то да, если 3 то уже сложно, а если добавится 4, 5 то пиши пропало. Да и как правило добавление нового параметра приводит к переделыванию всех предыдущих специализаций (так как в специализации мы не можем увеличивать число шаблонных параметров, а можем их только уменьшать).Если вас заинтересовало, прошу под кат, я покажу вам как добиться этогоНо для начала немного дёгтя. Шаблонные типы хороши тем, что пользователь может параметризовать их разными типами, в том числе и своими. Способ который я хочу показать не позволяет специализировать шаблон произвольным типом. Т.е. например вы определи свой тип deadline_super который сопоставим с типом deadline, тогда вы можете подставлять его в специализацию шаблона typedef some_container Но вы не сможете использовать этот тип в том механизме который позволят специализировать шаблон в произвольном виде. Пожалуй это единственное серьёзное ограничение. Учитывая этот аспект становится понятно, что этот механизм удобно использовать при написании модульных компонент, а не расширяемых или полиморфных.Вся реализация основывается на такой компоненте boost как mpl: set. Я не буду много рассказывать о том, что такое boost: mpl, скажу лишь что mpl: set позволяет создавать аналогичный std: set контейнер, но на этапе компиляции и состоящий из типов.Первое что нам потребуется это способ проверки списка типов на наличие нужного нам типа, и выдачу некоего дефолтного значения (struct disable) в противном случае template struct get_plugin_impl;

template struct get_plugin_impl { typedef disable_plugin type; };

template struct get_plugin_impl { typedef Plugin type; };

template struct get_plugin { typedef typename get_plugin_impl:: type>:: type type; }; Это позволит нам из списка типов узнать был ли задан нужный нам тип и если нет, то использовать дефолтное значение. Как видите данный код зависит только от одного типа который участвует в параметризации конечной структуры (и сообщает что мы не используем это конкретный тип), поэтому при добавлении новых шаблонных типов, этот код не будет меняться.На следующем шаге, мы определяем все типы которые используются в конечном шаблоне template struct get_plugins { typedef typename get_plugin:: type deferred; typedef typename get_plugin:: type deadline; }; И это то место, которое будет меняться при добавлении новых шаблонных параметров. Но это происходит предельно легко, и никаких 2^n комбинаций перечислять не надо.Дальше мы вводим прослойку между конечным шаблонным типом и пользователем template struct container { typedef boost: mpl: set plugin_list; typedef get_plugins plugs; typedef typename plugs: deferred deferred; typedef typename plugs: deadline deadline; typedef some_container 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 struct some_container { static const std: string& type () { static std: string type_(«some_container<" + Deferred::type() + ", " + Deadline::type() + ">»); return type_; } }; Использование cout << container:: type: type () << std::endl; cout << container:: type: type () << std::endl; cout << container:: type: type () << std::endl; cout << container:: type: type () << std::endl; cout << container:: type: type () << std::endl; Выхлоп some_containersome_containersome_containersome_containersome_container

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

© Habrahabr.ru