📜 ⬆️ ⬇️

Arbitrary order of template initialization list

I think many who work with templates are familiar with the following situation. We have a certain template class with a bunch of template parameters.

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

Let it be some kind of delayed task execution interface (active queue). And we want to expand its functionality by adding deferred execution and deadlines. But we do not want all this to be immediately turned on, but we want to be able to assemble the necessary configuration to fit our needs. The problem with such patterns (which have many parameters and almost all already have a default value) is that in order to redefine, say, the last parameter, we need to specify everything in front of it.

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

And even better, that even the order of the task would not matter and the next two would be equivalent
')
 typedef some_container<int, deferred, deadline> full_container1; typedef some_container<int, deadline, deferred> full_container1; 

But we are well aware that as soon as we change two parameters, we will have something completely different than what we expected (this is not a tuple for you where the order indication does not matter)
I think many have already thought that all this can be achieved by adding a layer between our type and the user, for which to write all possible specializations. If you have only 2 template parameters, then yes, if 3 is already difficult, and if 4, 5 is added, then write is gone. And, as a rule, the addition of a new parameter leads to the alteration of all previous specializations (since in specialization we cannot increase the number of template parameters, but we can only reduce them).
If you are interested, I ask under the cat, I will show you how to achieve this.

But to start a little bit of tar. Template types are good because the user can parameterize them with different types, including their own. The way I want to show does not allow to customize a template with an arbitrary type. Those. for example, you define your deadline_super type which is comparable to the deadline type, then you can substitute it in the template specialization

 typedef some_container<int, disable, deadline_super> 

But you can not use this type in the mechanism that will allow to specialize the template in any way. Perhaps this is the only serious limitation. Given this aspect, it becomes clear that this mechanism is convenient to use when writing modular components, rather than expandable or polymorphic.
The whole implementation is based on a boost component like mpl :: set . I will not talk much about what boost :: mpl is , let me just say that mpl :: set allows you to create a container similar to std :: set but at the compilation stage and consisting of types.
The first thing we need is a way to check the type list for the type we need, and issue some default value (struct disable) otherwise

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

This will allow us to find out from the list of types whether the type we need was set and if not, then use the default value. As you can see, this code depends on only one type that participates in the parameterization of the final structure (and reports that we do not use this particular type), therefore, when adding new template types, this code will not change.
In the next step, we define all types that are used in the final template.

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

And this is the place that will change when adding new template parameters. But this happens extremely easily, and no 2 ^ n combinations should be listed.
Next, we introduce an interlayer between the final template type and the user.

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

It allows us to abstract from the number and order of indication of template parameters. In essence, it combines all those (2 ^ n + K) specializations that we would have to write for a different number of specified template parameters and their order.

Returning to our template type, I'll show you how it works.

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

Using

  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; 

Exhaust

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

Please note that the order of the specialization is preserved in the desired form, regardless of the order in which the types were specified when instantiating the template.

That's all, I hope someone it will help make the code more beautiful and neat.
Source: git

Source: https://habr.com/ru/post/219999/


All Articles