Please don't get my "late binding" wrong, I don't mean usual late binding at runtime, I mean something else and cannot find a better word for it:
Consider I am working on a container (or similar) data structure Containor
for some value type V
that needs to compare these values with a comparator, so my first template looks like this
template<typename Val, typename Comp = std::less<Val>>
struct Containor{};
Now, my Containor
structure makes use of another container internally. Which container is to be used should be configurable by template arguments as well, lets say the default is std::set
. So my next version of Containor
looks like this:
template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set<Val,Comp>>
struct Containor{};
and here is where the code begins smelling IMHO. As long as the user is satisfied with the default implementation of the inner container, everything is fine. However, suppose he wants to use the new google btree set implementation btree::btree_set
instead of std::set
. Then he has to instanciate the template like this:
typedef Containor<int,std::less<int>,btree::btree_set<int,std::less<int>> MyContainor;
^^^^^^^^^^^^^^^^^^^
I have underlined the part where my problem lies. The CLIENT CODE has to instanciate the btree_set with the right parameters. This honestly sucks, because the Containor
class always needs a set of exactly the same type and comparator as its own first two template arguments. The client can - by accident - insert other types here! In addition, the client has the burden of choosing the right parameters. This might be easy in this case, but it hard if the inner container must for example be a set of pairs of the value type and some other type. Then the client has an even harder time getting the type parameters of the inner set correct.
So what I want is a way in which the client code only hands in the raw template and the Containor
internally instanciates it with the correct arguments, i.e. something like that:
template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set >
struct Containor{
typedef Cont<Val,Comp> innerSet;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ container instanciates the inner containor
};
typedef Containor<int,std::less<int>,btree::btree_set> MyContainor;
// ^^^^^^^^^^^^^^^^
// client only hands in raw template
Of course, this is no valid C++!
So I thought about ways to solve this problem. The only solution I could think of was writing "binder classes" for all data structures I want to use, like this:
struct btree_set_binder{
template<typename V, typename C = std::less<V>>
struct bind{
typedef btree::btree_set<V,C> type;
}
};
Now I can define my Containor
with a set binder
template<typename Val, typename Comp = std::less<Val>, typename ContBinder = btree_set_binder >
struct Containor{
typedef btree_set_binder::bind<Val,Comp>::type innerSet;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ works like a charm
};
Now, the user must only supply the desired binder class and the Containor
will instanciate it with the right arguments. So these binder classes would be okay for me, but it is quite a hassle writing binder classes for all containers. So is there a better or easier way to bind template arguments "late" in C++11, i.e., inside another template that retrieves the raw template as parameter.