If I have the following template parameters:
template <typename T_Key, typename T_Value, typename T_HashFunc,
typename T_ExtractKey, typename T_EqualKey, typename T_RehashClass, typename T_HashcodeClass>
class Hashtable { /* ... */ };
Where T_RehashClass and T_HashcodeClass are two template classes that take in T_Key, T_Value, T_HashFunc, T_ExtractKey and T_EqualKey as well. I would like those classes to grab the types from Hashtable's typedef list (all template parameter types in Hashtable are typedefined).
Note that T_RehashClass and T_HashcodeClass can be created by the user as well (defaults are provided) and can have other template parameters if the user wishes.
As it stands, any class that is a T_RehashClass must have T_Key, T_Value etc. template parameters filled out, which I see as code duplication. I would like the class to somehow have knowledge of Hashtable so that it can access its typedefs and deduce T_Key, T_Value etc. types automatically by creating its own typedefs. Unfortunately, in this case, I get a cyclic dependency.
How is this type of problem solved generally?
Also note that I am following EASTL where EASTL uses multiple inheritance to inherit from T_RehashClass and T_HashnodeClass instead and consequently, Hashtable has even more template parameters. I wondered if there was a way around it (i.e. not inheriting from both policies and have them as template parameters as inheriting from the policies reduces flexibility).
One solution that I thought of was to have a template struct that has all the template parameters from T_Key to T_EqualKey. The Hashtable declaration will then be:
template <typename T_HashtableParams, typename T_RehashClass = default_rehash_class<T_HashtableParams>, typename T_HashcodeClass = default_hashnode_class<T_HashtableParams> >
class Hashtable { /* ... */ };
The T_RehashClass and T_HashcodeClass can then take the defaults which will eliminate code duplication. Problem with this approach is that it is more cumbersome for the user to use.
I'm not sure it's terribly interesting to specify different T_Key and T_Value types for your hash and rehash classes. If I were going about this problem, I would try and set up a policy for the key/val first. My inclination is to say that there maybe should be a ValuePolicy instead of grouping it with KeyPolicy, but that is neither here nor there.
namespace hash {
namespace detail {
template<
typename Key
, typename Value
, typename KeyGetter
, typename KeyComparator>
class KeyPolicy {
public:
typedef Key key_t;
typedef Value value_t;
typedef KeyGetter get_t;
typedef KeyComparator compare_t;
};
}} // hash detail
The HashTable isn't valid unless it has the same KeyPolicy as the rehast, etc so don't give it one.
namespace hash {
namespace detail {
template<typename RehashPolicy>
class HashTableImpl {
public:
typedef RehashPolicy rehash_p;
typedef typename rehash_p::key_policy_t::key_t key_t;
// typedef ...
};
// this doesn't have a specific name, its anything.
template<typename KeyPolicy>
class SomeRehashPolicy {
public:
typedef KeyPolicy key_policy_t;
};
}} // hash detail
You can add whatever typedefs you want there, obviously. If I were a stickler in a code review I'd probably ask for things like rehash_p and key_policy_t to be private. They are implementation details, really. The actual invariant you are trying to protect are what are in key_t, etc.
Maybe I'm outside the reasonable bounds of etiquette, but my honest opinion is that all this configuration is only interesting to the guy that wrote it. Not you, not anyone using it. So I would only expose the HashTable configuration or two that people are actually going to use.
namespace hash {
struct stub {}; // sorry, I'm lazy
template<typename Key, typename Value>
class HashTable {
private:
typedef typename detail::KeyPolicy<Key, Value, stub, stub> key_p;
typedef typename detail::SomeRehashPolicy<key_p> rehash_p;
typedef typename detail::HashTableImpl<rehash_p> impl_t;
public:
typedef typename impl_t::key_t key_t;
};
} // hash
int main(int argc, char ** argv) {
hash::HashTable<int, double> hash_table;
return 0;
}
Lots of details aren't filled in obviously, but you get the idea.
See e.g. Modern C++ Design (sec. 1.5.1: Implementing Policies with template-template parameters). The idea is to let T_RehashClass and T_HashcodeClass be template-template parameters. These classes take the T_Key, T_Value and whatever else as their own parameters. To avoid retyping, you can then inherit the needed instantiation of those templates.
template <
typename T_Key,
typename T_Value,
typename T_HashFunc,
typename T_ExtractKey,
typename T_EqualKey,
template<typename, typename /*, more params */> class T_RehashClass,
template<typename, typename /*, more params */> class T_HashcodeClass
> class Hashtable
:
public T_RehashClass<T_Key, T_Value /*, more params */>,
public T_HashcodeClass<T_Key, T_Value /*, more params */>
{ /* ... */ };
NOTE: you really need "class" and not "typename" in front of T_RehashClass and T_HashcodeClass because they are template names, not template types!
Related
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.
Maybe make your own comparator trait.
// Comparator trait primary template
template <typename T> stuct MyComparator
{
typedef typename T::key_compare type;
};
// Comparator trait non-standard usage example
template <typename U, typename V, int N>
struct MyComparator<WeirdContainer<U, V, N>>
{
typedef std::greater<V> type;
};
template <typename T, typename Cont = std::set<T>>
struct MyAdaptor
{
typedef typename MyComparator<Cont>::type comparator_type;
typedef T value_type;
// ...
};
I've renamed your "Containor" to "MyAdaptor", since this sort of construction is usually called an "adaptor" class.
Usage:
MyAdaptor<int> a; // uses std::set<int> and std::less<int>
MyAdaptor<double, WeirdContainer<bool, double, 27>> b;
Update: In light of the discussion, you could even remove the outer type argument entirely:
template <typename Cont> struct MyBetterAdaptor
{
typedef MyAdaptorTraits<Cont>::value_type value_type;
typedef MyAdaptorTraits<Cont>::pred_type pred_type;
// ...
};
To be used like this:
MyBetterAdaptor<std::set<int>> c; // value type "int", predicate "std::less<int>"
Writing the MyAdaptorTraits template is left as an exercise.
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,
So what you need is obviously a template template parameter:
// std::set has three template parameters,
// we only want to expose two of them ...
template <typename V, typename C>
using set_with_defalloc = std::set<V,C>;
template<
typename Val,
typename Comp = std::less<Val>,
template <typename V, typename C> class Cont = set_with_defalloc>
struct Containor{
typedef Cont<Val,Comp> innerSet;
// ...
};
You should be able to do it with template template parameters, as in:
template<typename Val, typename Comp = std::less<Val>, template <typename...> class ContBinder = std::set>
struct Containor {
typedef ContBinder<Val, Comp> innerSet;
// ...
};
Note: you need the variadic typename... because std::set takes three template parameters (the third being an allocator) while other containers might not.
Assume I have a template (called ExampleTemplate) that takes two arguments: a container type (e.g. list, vector) and a contained type (e.g. float, bool, etc). Since containers are in fact templates, this template has a template param. This is what I had to write:
#include <vector>
#include <list>
using namespace std;
template < template <class,class> class C, typename T>
class ExampleTemplate {
C<T,allocator<T> > items;
public:
....
};
main()
{
ExampleTemplate<list,int> a;
ExampleTemplate<vector,float> b;
}
You may ask what is the "allocator" thing about. Well, Initially, I tried the obvious thing...
template < template <class> class C, typename T>
class ExampleTemplate {
C<T> items;
};
...but I unfortunately found out that the default argument of the allocator...
vector<T, Alloc>
list<T, Alloc>
etc
...had to be explicitely "reserved" in the template declaration.
This, as you can see, makes the code uglier, and forces me to reproduce the default values of the template arguments (in this case, the allocator).
Which is BAD.
EDIT: The question is not about the specific problem of containers - it is about "Default values in templates with template arguments", and the above is just an example. Answers depending on the knowledge that STL containers have a "::value_type" are not what I am after. Think of the generic problem: if I need to use a template argument C in a template ExampleTemplate, then in the body of ExampleTemplate, do I have to reproduce the default arguments of C when I use it? If I have to, doesn't that introduce unnecessary repetition and other problems (in this case, where C is an STL container, portability issues - e.g. "allocator" )?
Perhaps you'd prefer this:
#include <vector>
#include <list>
using namespace std;
template <class Container>
class ForExamplePurposes {
typedef typename Container::value_type T;
Container items;
public:
};
int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
}
This uses "static duck typing". It is also a bit more flexible as it doesn't force the Container type to support STL's Allocator concept.
Perhaps using the type traits idiom can give you a way out:
#include <vector>
#include <list>
using namespace std;
struct MyFunkyContainer
{
typedef int funky_type;
// ... rest of custom container declaration
};
// General case assumes STL-compatible container
template <class Container>
struct ValueTypeOf
{
typedef typename Container::value_type type;
};
// Specialization for MyFunkyContainer
template <>
struct ValueTypeOf<MyFunkyContainer>
{
typedef MyFunkyContainer::funky_type type;
};
template <class Container>
class ForExamplePurposes {
typedef typename ValueTypeOf<Container>::type T;
Container items;
public:
};
int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
ForExamplePurposes< MyFunkyContainer > c;
}
Someone who wants to use ForExamplePurposes with a non-STL-compliant container would need to specialize the ValueTypeOf traits class.
I would propose to create adapters.
Your class should be created with the exact level of personalization that is required by the class:
template <template <class> C, template T>
class Example
{
typedef T Type;
typedef C<T> Container;
};
EDIT: attempting to provide more is nice, but doomed to fail, look at the various expansions:
std::vector<T>: std::vector<T, std::allocator<T>>
std::stack<T>: std::stack<T, std::deque<T>>
std::set<T>: std::set<T, std::less<T>, std::allocator<T>>
The second is an adapter, and so does not take an allocator, and the third does not have the same arity. You need therefore to put the onus on the user.
If a user wishes to use it with a type that does not respect the expressed arity, then the simplest way for him is to provide (locally) an adapter:
template <typename T>
using Vector = std::vector<T>; // C++0x
Example<Vector, bool> example;
I am wondering about the use of parameter packs (variadic templates) here... I don't know if declaring C as template <class...> C would do the trick or if the compiler would require a variadic class then.
You have to give the full template signature, including default parameters, if you want to be able to use the template template parameter the usual way.
template <typename T, template <class U, class V = allocator<U> > class C>
class ExampleTemplate {
C<T> items;
public:
....
};
If you want to handle other containers that the one from the STL, you can delegate container construction to a helper.
// Other specialization failed. Instantiate a std::vector.
template <typename T, typename C>
struct make_container_
{
typedef std::vector<T> result;
};
// STL containers
template <typename T, template <class U, class V = allocator<U> > class C>
struct make_container_<T,C>
{
typedef C<T> result;
};
// Other specializations
...
template <typename T, typename C>
class ExampleTemplate {
make_container_<T,C>::result items;
public:
....
};
I think, it is required to reproduce all template parameters, even default. Note, that Standard itself does not use template template parameters for containter adaptors, and prefers to use regular template parameters:
template < class T , class Container = deque <T > > class queue { ... };
template < class T , class Container = vector <T>, class Compare = less < typename Container :: value_type > > class priority_queue { ... };
The following code will allow you to do something like you're asking for. Of course, this won't work with standard containers, since this has to already be part of the template class that's being passed into the template.
/* Allows you to create template classes that allow users to specify only some
* of the default parameters, and some not.
*
* Example:
* template <typename A = use_default, typename B = use_default>
* class foo
* {
* typedef use_default_param<A, int> a_type;
* typedef use_default_param<B, double> b_type;
* ...
* };
*
* foo<use_default, bool> x;
* foo<char, use_default> y;
*/
struct use_default;
template<class param, class default_type>
struct default_param
{
typedef param type;
};
template<class default_type>
struct default_param<use_default, default_type>
{
typedef default_type type;
};
But I don't really think this is what you're looking for. What you're doing with the containers is unlikely to be applicable to arbitrary containers as many of them will have the problem you're having with multiple default parameters with non-obvious types as defaults.
As the question exactly described the problem I had in my code (--I'm using Visual Studio 2015), I figured out an alternative solution which I wanted to share.
The idea is the following: instead of passing a template template parameter to the ExampleTemplate class template, one can also pass a normal typename which contains a type DummyType as dummy parameter, say std::vector<DummyType>.
Then, inside the class, one replace this dummy parameter by something reasonable. For replacement of the typethe following helper classes can be used:
// this is simply the replacement for a normal type:
// it takes a type T, and possibly replaces it with ReplaceByType
template<typename T, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type
{
using type = std::conditional_t<std::is_same<T, ReplaceWhatType>::value, ReplaceByType, T>;
};
// this sets up the recursion, such that replacement also happens
// in contained nested types
// example: in "std::vector<T, allocator<T> >", both T's are replaced
template<template<typename ...> class C, typename ... Args, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type<C<Args ...>, ReplaceWhatType, ReplaceByType>
{
using type = C<typename replace_type<Args, ReplaceWhatType, ReplaceByType>::type ...>;
};
// an alias for convenience
template<typename ... Args>
using replace_type_t = typename replace_type<Args ...>::type;
Note the recursive step in replace_type, which takes care that types nested in other classes are replaced as well -- with this, for example, in std::vector<T, allocator<T> >, both T's are replaced and not only the first one. The same goes for more than one nesting hierarchy.
Next, you can use this in your ExampleTemplate-class,
struct DummyType {};
template <typename C, typename T>
struct ExampleTemplate
{
replace_type_t<C, DummyType, T> items;
};
and call it via
int main()
{
ExampleTemplate<std::vector<DummyType>, float> a;
a.items.push_back(1.0);
//a.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::list<DummyType>, float> b;
b.items.push_back(1.0);
//b.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::map<int, DummyType>, float> c;
c.items[0]=1.0;
//c.items[0]="Hello"; // prints an error message which shows that DummyType is replaced correctly
}
DEMO
Beside the not-that-nice syntac, this has the advantage that
It works with any number of default template parameters -- for instance, consider the case with std::map in the example.
There is no need to explicitly specify any default template parameters whatsoever.
It can be easily extended to more dummy parameters (whereas then it probably should not be called by users ...).
By the way: Instead of the dummy type you can also use the std::placeholder's ... just realized that it might be a bit nicer.
I have a set of classes in a logging framework used by project A and B. I am refactoring the framework so it can be used in project B and C. The refactoring mainly consists of giving everything template parameters: project A might run on an embedded device with poor/no STL implemenatation, while B and C just run on a pc, but B is single threaded while C uses multithreading.
This works well, but results in what seems to me an awfull lot of template parameters and a rather ugly typedef mess. I need like 20 lines to typedef all classes I'm going to use, and there are also lots of classes taking a template parameter they do not use themselves, but is needed to be able to typedef another class they do use (which is not per se a bad thing, but in the end everything starts to llok really complicated). Another problem is that when I want to add some functionality to class A and it requires adding a container, class A needs an extra template paramater. As a result, all other classes seeing/using class A suddenly also need that extra parameter leading to a domino effect.
Slightly exaggerated example:
template< class string, class map, class mutex >
class MessageDestination
{
typedef Message< string, map > message_type;
virtual void Eat( const message_type& ) = 0;
}
template< class string, class map, class stream >
class MessageFormatter
{
typedef Message< string, map > message_type;
virtual void Format( const message_type&, stream& ) = 0;
}
template< class string, class map, class containerA,
template< class, class > containerB, template< class, class > class queue, class allocator >
class ThreadedMessageAcceptor
{
typedef Message< string, map > message_type;
typedef MessageDestination< string, map > destination_type;
typedef containerB< destination_type, allocator > destinations_type;
typedef queue< message_type, allocator > messages_type;
};
I can think of some techniques to clean this up, but I'm having a hard time deciding what one or which combination to use. StackOverFlow, your help will be appreciated!
Here's the first solution I thought of, joining parameters together into the type they'll eventually form:
template< class message, class mutex >
class MessageDestination
{
virtual void Eat( const message& ) = 0;
}
This makes it simpler, but doesn't it at the same time it kind of hides what message actually is? Suppose a user wants to provide an implementation, he does not directly see that message has to use a certain string type etc.
Another technique I thougt about, but cannot recall having seen before somwhere which makes it look suspicious, is simply defining everything in a single struct and passing that as single template parameter to everything:
struct MyTemplateParameters
{
typedef std::string string;
typedef std::map map;
typedef std::queue queue;
typedef LightMutex mutex;
template< class A, class B >
struct DefineContainerB
{
typedef containerB< A, B >::type;
}
//....
};
template< class parameters >
class MessageDestination
{
typedef Message< parameters > message_type;
virtual void Eat( const message_type& ) = 0;
};
template< class parameters >
class ThreadedMessageAcceptor
{
typedef Message< parameters > message_type;
typedef MessageDestination< parameters > destination_type;
typedef parameters::DefineContainerB< destination_type, parameters::allocator >::type destinations_type;
};
This is nice as it allows specifying everything at one single point, and the typedefs to all classes will all be class XXX< MyTemplateParameters >, but again, it gives me an uneasy feeling. Is there a reason for this?
The “other” technique is very common in C++. The parameters class is usually called a “trait class”.
This is the way to go (why does it give you an uneasy feeling?). It is used pervasively in the Boost libraries and other C++ libraries. Even the standard library uses it, e.g. in the std::basic_string class.
An equally well-established alternative are metafunctions. At its most basic, a metafunction is a “function” that operates on types rather than objects. So where a function takes value arguments and returns a value, a metafunctions takes template arguments and “returns” a type:
template <typename T>
struct identity {
typedef T type;
};
The metafunction is used (“invoked”) like a normal type definition.
typedef identity<int>::type mytype; // or
identity<int>::type x;
Not very useful in this case. But consider the following common metafunction:
template <typename T>
struct remove_const {
typedef T type;
};
template <typename T>
struct remove_const<T const> {
typedef T type;
};
This can be used to make an arbitrary type (in particular a template argument) non-const. This is actually a type I’m currently using in a project: I have a class that accepts both const and non-const types and offers appropriate interfaces. However, internally I need to store a non-const reference. Simple, I just use the following code in my class:
typename remove_const<T>::type& _reference;
(The typename is required because T is a template argument and that makes remove_const<T>::type a dependent type. Your above example code is actually omitting quite a few required typenames – it won’t compile on several modern compilers!)
Now, how to apply this to your problem?
Create two empty marker types that specify whether your types are used on an embedded device or on a compliant compiler:
struct Embedded { };
struct Compliant { };
Now you can define your classes in terms of these, e.g.:
template<typename Spec>
class ThreadedMessageAcceptor
{
typedef Message< Spec > message_type;
typedef MessageDestination< Spec > destination_type;
typedef typename Allocator< destination_type, Spec >::type allocator_type;
typedef typename ContainerB< destination_type, allocator_type, Spec >::type destinations_type;
};
Here, Spec will be either Compliant or Embedded. So to use it on a standards compliant compiler, write:
ThreadedMessageAcceptor<Compliant> x;
The class uses the following metafunctions:
template <typename T, typename Spec>
struct Allocator { };
template <typename T, typename Alloc, typename Spec>
struct ContainerB { };
You need to remember to specialize them appropriately for your target specifications, e.g.:
template <typename T>
struct Allocator<T, Compliant> {
typedef std::allocator<T> type;
};
template <typename T, typename Alloc>
struct ContainerB<T, Alloc, Compliant> {
typedef std::vector<T, Alloc> type;
};
This already shows that a metafunction may have arbitrarily many arguments besides the Spec (which I’ve put last on a whim – but its placement should be consistent).
To be sure, this is more code than when using a single trait class but it has lower cohesion, logically separates concerns and is easier to reuse.
Hard to come up with a good title for this question. What I really need is to be able to provide template parameters with different number of arguments in place of a single parameter. Doesn't make a lot of sense so I'll go over the reason:
template < typename T, template <typename,typename> class Policy = default_policy >
struct policy_based : Policy<T, policy_based<T,Policy> >
{
// inherits R Policy::fun(arg0, arg1, arg2,...,argn)
};
// normal use:
policy_base<type_a> instance;
// abnormal use:
template < typename PolicyBased > // No T since T is always the same when you use this
struct custom_policy {};
policy_base<type_b,custom_policy> instance;
The deal is that for many abnormal uses the Policy will be based on one single type T, and can't really be parameterized on T so it makes no sense to take T as a parameter. For other uses, including the default, a Policy can make sense with any T.
I have a couple ideas but none of them are really favorites. I thought that I had a better answer--using composition instead of policies--but then I realized I have this case where fun() actually needs extra information that the class itself won't have.
This is like the third time I've refactored this silly construct and I've got quite a few custom versions of it around that I'm trying to consolidate. I'd like to get something nailed down this time rather than just fish around and hope it works this time. So I'm just fishing for ideas right now hoping that someone has something I'll be so impressed by that I'll switch deities. Anyone have a good idea?
Edit: You might be asking yourself why I don't just retrieve T from the definition of policy based in the template for default_policy. The reason is that default_policy is actually specialized for some types T. Since asking the question I have come up with something that may be what I need, which will follow, but I could still use some other ideas.
template < typename T >
struct default_policy;
template < typename T, template < typename > class Policy = default_policy >
struct test : Policy<test<T,Policy>>
{};
template < typename T >
struct default_policy< test<T, default_policy> >
{
void f() {}
};
template < >
struct default_policy< test<int, default_policy> >
{
void f(int) {}
};
Edit:
Still messing with it. I wasn't too fond of the above since it makes default_policy permanently coupled with "test" and so couldn't be reused in some other method, such as with multiple templates as suggested below. It also doesn't scale at all and requires a list of parameters at least as long as "test" has. Tried a few different approaches that failed until I found another that seems to work so far:
template < typename T >
struct default_policy;
template < typename T, template < typename > class Policy = default_policy >
struct test : Policy<test<T,Policy>>
{};
template < typename PolicyBased >
struct fetch_t;
template < typename PolicyBased, typename T > struct default_policy_base;
template < typename PolicyBased >
struct default_policy : default_policy_base<PolicyBased, typename fetch_t<PolicyBased>::type> {};
template < typename T, template < typename > class Policy >
struct fetch_t< test<T,Policy> > { typedef T type; };
template < typename PolicyBased, typename T >
struct default_policy_base
{
void f() {}
};
template < typename PolicyBased >
struct default_policy_base<PolicyBased,int>
{
void f(int) {}
};
I had a similar problem, and did not find a desirable answer. From what I know, C++ doesn't have elegant support for variable amounts of template arguments, so you have to get around it by "wrapping" extra arguments in another class;
policy_base< twoArgs<part1, part2> >
You can pretty this up a little bit by putting making typedefs for twoArgs, but not by a lot in this basic case. (Remember, you can "do" templated typedefs by using member typedefs in templated classes.)
or by having many different declarations of the core template, with different args;
template< typename T1 > struct base {...}
template< typename T1, typename t2 > struct base {...}
//etc
Have you looked at so-called Named Template Parameters? This allows you to have many parameters with a hidden default, each of which can be overridden by name (i.e. in arbitrary order). It's a trick relying on multiple levels of indirection and multiple inheritance. It's described in chapter 16.1 of the book "Templates the Complete Guide" by Vandevoorde & Josuttis. For an online exposition see here. The idea is implemented in Boost.Parameter
Here's a short summary for the Boost.Parameter library.
Step 1) you declare each parameter par0 through parN through the following macros:
BOOST_PARAMETER_TEMPLATE_KEYWORD(par0)
BOOST_PARAMETER_TEMPLATE_KEYWORD(par1)
// ...
BOOST_PARAMETER_TEMPLATE_KEYWORD(parN)
Each macro will define both a regular class par0 in namespace tag and a class template par0 at namespace scope
namespace tag { struct par0; } // keyword tag type
template <class T>
struct par0
:
parameter::template_keyword<tag::par0, T>
{};
Step 2) you declare your class signature with the required and optional parameters:
using boost::mpl::_;
typedef parameter::parameters<
parameter::required<tag::par0>
, parameter::optional<tag::par1>
// ...
, parameter::optional<tag::parN>
> your_signature;
Step 3) you declare your policy class
template <
class a0
, class a1 = parameter::void_
// ...
, class aN = parameter::void_
>
struct your_policy
{
// Create ArgumentPack
typedef typename
your_signature::bind<a0, a1, /* ... */ ,aN>::type
args;
typedef typename parameter::value_type<
args, tag::par0>::type par0;
typedef typename parameter::value_type<
args, tag::par1, your_par1_default>::type par1;
// ...
typedef typename parameter::value_type<
args, tag::parN, your_parN_default>::type parN;
};
Step 4) define your policies and specializations
template<typename T>
class default_policy
:
your_policy<T> // all hidden template parameters equal to their defaults
{};
template<typename T>
class some_par2_specialized_policy
:
your_policy<T, par2<some_override_for_par2> > // note par1 does not have to be overriden!!
{};
For a flexible and scalable policy-based design, my experience is that Boost.Parameter works like a charm (you might need to override BOOST_PARAMETER_MAX_ARITY if you have more than 8 hidden parameters though).
It should be possible to adapt your design to this technique. Of course, once you introduce extra template parameters that have hidden defaults, you need to pick off the cases where they are overridden the code that uses your policy class. This is no different than when you use multiple traits to communicate your specialized behavior to client code, except that with policies you have everything nicely wrapped together.
A C++ hash_map has the following template parameters:
template<typename Key, typename T, typename HashCompare, typename Allocator>
How can I specify a Allocator without specifying the HashCompare?
This won't compile :(
hash_map<EntityId, Entity*, , tbb::scalable_allocator>
The simple answer is that you can't. You cannot skip a template parameter and have it choose the default value for the template. Your only option is to find out what the default is and insert it into your declaration.
There's one trick you can use which will at least save you having to work out what the default is, but it does require that you know the name of the type as it is defined in hash_map.
The hash_map will probably be declared something like:
class allocator {};
class hash_compare {};
template<typename Key
, typename T
, typename HashCompare = hash_compare
, typename Allocator = allocator>
class hash_map
{
public:
typedef HashCompare key_compare;
// ...
};
We cannot leave out the default for the hash, but we can refer to the default using the member typedef:
hash_map<EntityId
, Entity*
, hash_map<EntityId,Entity*>::key_compare // find out the default hasher
, tbb::scalable_allocator> hm;
If you're going to use the type a lot, then create a typedef:
typedef hash_map<EntityId,Entity*>::key_compare EntityKeyCompare;
hash_map<EntityId
, Entity*
, EntityKeyCompare
, tbb::scalable_allocator> hm;
If the has map type has some public typedef for the HashCompare template parameter, you could write a meta function that uses a vanilla hash map type to get the std comparator. Something like this:
template< typename Key, typename T, typename Allocator>
struct hash_map_type {
typedef typename hash_map<Key,T>::key_compare key_compare;
typedef mash_map<Key,T,key_compare,Allocator> result_t;
};
typedef hash_map_type<int,string,my_allocator>::result_type my_hash_map;
This, however, depends on something like the above hash_map<Key,T>::key_compare being accessible.