cleaning up heavy use of template parameters - c++

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.

Related

Recursive Compound structure

I have a class that looks something like this:
template<class KeyType, class... Types>
class BasicCompound
{
public:
using mapped_type = std::variant
<
ValueWrapper<BasicCompound>
, ValueWrapper<Types>...
>;
using key_type = KeyType;
// Accessors for retreiving and modifying content
// ...
private:
std::map<key_type, mapped_type> m_content;
};
ValueWrapper decides to put the content either inline or in a std::unique_ptr. Would it be possible with a similar interface, possibly through some kind of proxy, to make the recursiveness optional? By optional I mean that the user should not automatically get the possibility to store the BasicCompound inside itself, but rather specify it in the list of types.
What I have thought of:
A using directive does not work. A new type cannot be defined in it self, and a predeclaration of a following typedef is not allowed.
Adding a bool to the list of types, and use std::conditional_t for mapped_type. However, if the user wants to store an X<BasicCompound>, this approach fails.
Inject mapped_type from outside. Then I cannot hide the use of the ValueWrapper thing.
Using inheritance over a typdef like
struct MyCompound : BasicCompound<std::string, MyCompound, int> {};
This works but then the structure is not strictly recursive, as MyCompound now is a different type from BasicCompound. Maybe a CRTP-like approach could solve that problem, but then the inner compound type must be treated differently than the other types.
You can't have use a specialization of a class template as one of the template arguments of that specialization; there's no way to write it, and its name would be infinitely long. You can, however, use a wrapper to hold the recursive type:
template<class> class MaybeRecursive;
template<class T>
struct Wrapper {
using type=T;
};
struct MRWrapper {
using type=MaybeRecursive<MRWrapper>;
};
template<class T>
struct MaybeRecursive {
using type=typename T::type;
type *child;
};
void f() {
int x;
MaybeRecursive<Wrapper<int>> nonrec={&x};
MRWrapper::type rec={&rec};
}
MRWrapper can be made a class template to provide additional template arguments to MaybeRecursive.

Reducing template policy clutter

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!

Default values in templates with template arguments ( C++ )

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.

Policy based design and defaults

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.

C++: Default values for template arguments other than the last ones?

I have my templated container class that looks like this:
template<
class KeyType,
class ValueType,
class KeyCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<KeyType>,
class ValueCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<ValueType>
>
class MyClass
{
[...]
}
Which means that when I instantiate an object of this class, I can do it several different ways:
MyClass<MyKeyType, MyValueType> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor, MyCustomValueCompareFunctor> myObject;
Those are all good. The problem comes when I want to instantiate a MyClass that uses a non-default version of the ValueCompareFunctor argument, but I still want to use the default value of the KeyCompareFunctor argument. Then I have to write this:
MyClass<MyKeyType, MyValueType, AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>, MyCustomValueCompareFunctor> myObject;
It would be much more convenient if I could somehow omit the third argument and just write this:
MyClass<KeyType, ValueType, MyCustomValueCompareFunctor> myObject;
Since the MyCustomValueCompareFunctor works only on objects of type MyValueType and not on objects of type MyKeyType, it seems like the compiler could at least theoretically work out what I meant here.
Is there a way to do this in C++?
In general, both in templates and functions or methods, C++ lets you use default for (and thereby omit) only trailing parameters -- no way out.
I recommend a template or macro to shorten AnObnoxiouslyLongSequenceOfCharacters<MyKeyType> to Foo<MyKeyType> -- not perfect, but better than nothing.
No. The closest you can come is to allow users to specify some sentinel type - like void - meaning "use default value here", and use template metamagic inside your class to typedef the real default if void was given to you. But this probably isn't a good idea from readability point of view.
Boost parameters and Boost graph named parameters are efforts towards naming parameters for template functions/methods. They give the opportunity to provide arguments in whichever order you prefer. Some arguments may be optional, with default values.
The same approach may be applied to template arguments. Instead of having N template arguments + P optional ones, create your class with N+1 template arguments. The last one will hold "named" parameters which can be omitted.
This answer is not complete yet, but i hope it's a good start !
An alternative option is to use Traits classes:
template <class KeyType>
class KeyTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> Compare;
};
template <class ValueType>
class ValueTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> Compare;
};
template<class KeyType class ValueType>
class MyClass
{
typedef KeyTraits<KeyType>::Compare KeyCompareFunctor;
typedef ValueTraits<ValueType>::Compare KeyCompareFunctor;
};
Then if you have a type which needs a different comparison function for Key's, then you'd explicitly specialize the KeyTraits type for that case. Here's an example where we change it for int:
template <>
class KeyTraits<int>
{
typedef SpecialCompareForInt Cmopare;
};
There is another option, which uses inheritance and which works like the following. For the last two arguments, it uses a class that inherits virtually from a class that has two member templates, that can be used to generate the needed types. Because the inheritance is virtual, the typedefs it declares are shared among the inheritance as seen below.
template<class KeyType,
class ValueType,
class Pol1 = DefaultArgument,
class Pol2 = DefaultArgument>
class MyClass {
typedef use_policies<Pol1, Pol2> policies;
typedef KeyType key_type;
typedef ValueType value_type;
typedef typename policies::
template apply_key_compare<KeyType>::type
key_compare;
typedef typename policies::
template apply_value_compare<ValueType>::type
value_compare;
};
Now, have a default argument that you use, which has typedefs for the default arguments you want provide. The member templates will be parameterized by the key and value types
struct VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType>
type;
};
template<typename ValueType>
struct apply_value_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType>
type;
};
};
struct DefaultArgument : virtual VirtualRoot { };
template<typename T> struct KeyCompareIs : virtual VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef T type;
};
};
template<typename T> struct ValueCompareIs : virtual VirtualRoot {
template<typename ValueType>
struct apply_value_compare {
typedef T type;
};
};
Now, use_policies will derive from all the template arguments. Where a derived class of VirtualRoot hides a member from the base, that member of the derived class is dominant over the member of the base, and will be used, even though the base-class member can be reached by other path in the inheritance tree.
Note that you don't pay for the virtual inheritance, because you never create an object of type use_policies. You only use virtual inheritance to make use of the dominance rule.
template<typename B, int>
struct Inherit : B { };
template<class Pol1, class Pol2>
struct use_policies : Inherit<Pol1, 1>, Inherit<Pol2, 2>
{ };
Because we potentially derive from the same class more than once, we use a class template Inherit: Inheriting the same class directly twice is forbidden. But inheriting it indirectly is allowed. You can now use this all like the following:
MyClass<int, float> m;
MyClass<float, double, ValueCompareIs< less<double> > > m;