Is there a way to bind a template<template> parameter?
Asked Answered
C

1

8

Context

I have a custom comparator that takes another comparator and applies an additional check:

template <template <typename> class Comparator, typename T>
struct SoftOrder : public std::binary_function<T, T, bool> {
    bool operator()(const T lhs, const T rhs) const {
        return Comparator<T>()(lhs, rhs) && AnotherCheck();
    }
};

I have a second class that accepts a comparator, e.g.:

template <template <typename> class Comparator>
class Processor { ... };

It is easy to instantiate a Processor with a standard comparator (e.g. std::less) like so:

Processor<std::less> processor1;
Processor<std::greater> processor2;

However it is not so easy to instantiate with SoftOrder as the compiler correctly complains about the missing second template argument:

Processor<SoftOrder<std::less> > processor3; // <-- Fails to compile

Current Solutions

I have come up with a few solutions prior to posting this question.

First Solution - Lots of Derived Classes

template <typename T>
struct SoftOrderLessThan : public SoftOrder<std::less, T> {};

template <typename T>
struct SoftOrderGreaterThan : public SoftOrder<std::greater, T> {};

The main drawback of this solution is the need to create a new struct every time a new variant is required, e.g.:

template <typename T>
struct SoftOrderLessThan : public SoftOrder<std::less, T> {}; // Never used after the next line.
Processor<SoftOrderLessThan> processor3;

Second Solution - A very specific bind class

template <template <typename> class Comparator>
struct BindToSoftOrder {
    template <typename T>
    struct type : public SoftOrder<Comparator, T> {};
};

This is slightly better in that we don't need to create the intermediate classes explicitly:

Processor<BindToSoftOrder<std::less>::type> processor3;

The downside is the requirement of a class specialised for this situation which cannot really be generalised by making SoftOrder a template parameter on BindToSoftOrder as this would make it a template<template<template>>> which is not permitted by the standard.

Third Solution - C++11 template aliases

template <typename T>
using SoftOrderLessThan = SoftOrder<std::less, T>;

Nicer than the first option in that it doesn't require the introduction of new classes, however still requires littering the code with this extra code that is only used in passing onwards to another template class:

template <typename T>
using SoftOrderLessThan = SoftOrder<std::less, T>; // Never used again
Processor<SoftOrderLessThan> processor3;

Finally, the question

Is there a generic way to bind my custom comparator to a specific comparator in the following manner?

Processor<SomeCoolMetaTemplateBind<SoftOrder, std::less>::type> processor3;

I believe if all of the template parameters were simple types I could just do something like Processor<boost::mpl::bind<SoftOrder, std::less> >, but the presence of the template type in the template parameter list prevents this from occurring.

An ideal solution would involve C++03, but am happy to hear C++11 solutions as well.

If it's not possible, I hope at least the question was interesting.

Camshaft answered 7/7, 2012 at 17:44 Comment(2)
Is there anything legitimate that requires for Process to expect a template template parameter? Can you use a type parameter instead, where clients would use e.g. Processor<std::less<T>>? Then in turn SoftOrder can accept a type parameter, too. An example of this are the Standard container adaptors like std::stack -- you pass in std::stack<T, std::deque<T>>, not std::stack<T, std::deque>. Template template parameters usually make a design less expressive or flexible.Alic
@LucDanton it was something I was considering, but it become a matter of honour between me and the compiler and I was sure I'd get the upper hand on it eventually. Though as VaughnCato pointed out in his answer, my assumption in solution 2 above regarding the standard was plain old wrong anyway so I guess the compiler got the last laugh...Camshaft
O
3

Seems like this would work:

template <
  template <template <typename> class,class> class U,
  template <typename> class X
>
struct SomeCoolMetaTemplateBind {
  template <typename T>
  struct type : public U<X,T> {
  };
};
Officer answered 7/7, 2012 at 17:56 Comment(1)
That works a charm. For some reason I thought template<template<template ... wasn't permitted in the standard. Do you know if there is any generic boost metafunction that does this?Camshaft

© 2022 - 2024 — McMap. All rights reserved.