Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Altering template template parameters in C++

I would like to design a class that creates internal types that are variants of types passed as template parameters. Something like the following, non-functional example:

template <typename T>
class BaseClass
{
public:
    typedef T InternalType;
    std::vector<InternalType> storage;
};

template <typename Base>
class Injector
{
public:
    typedef std::pair<typename Base::InternalType, typename Base::InternalType> RefinedType;
    Base<RefinedType> refinedStorage;
};

typedef Injector<BaseClass<int> > InjectedInt; // Should store vector<pair<int, int> >

Since Base is a fully-specified type, Base<RefinedType> refinedStorage; will fail to compile. Simply using a template template parameter won't work, as the refined type needs to be based on the nested template's parameter as well as its base type.

How can I implement this pattern of creating types based on both the fully-specified and base types of a template parameter?

EDIT: I would like this to be an arbitrary-depth composite, with multiple injector types performing a cascade of transformations. Thus, passing both the template template parameter and the base parameter becomes pretty unwieldy (particularly when it comes to dealing with the base case of the composite), and an ideal solution would use the more direct syntax.

like image 204
professional_yet_not_trackable Avatar asked Jul 13 '15 13:07

professional_yet_not_trackable


4 Answers

I was able to achieve this by explicitly 're-declaring' the general template inside itself:

template <typename T>
class BaseClass
{
public:
    typedef T InternalType;
    std::vector<InternalType> storage;

    template<class T2>
    using Recur = BaseClass<T2>;
};

template <typename Base>
class Injector
{
public:
    typedef std::pair<typename Base::InternalType, typename Base::InternalType> RefinedType;
    typename Base::template Recur<RefinedType> refinedStorage;
};

typedef Injector<BaseClass<int> > InjectedInt; // Should store vector<pair<int, int> >
like image 82
Petr Avatar answered Oct 05 '22 18:10

Petr


You could introduce a rebind template:

template <typename From, typename To>
struct rebind_1st;

template <template <typename... > class Cls, typename A0, typename... Args, typename To>
struct rebind_1st<Cls<A0, Args...>, To> {
    using type = Cls<To, Args...>;
};

template <typename From, typename To>
using rebind_1st_t = typename rebind_1st<From, To>::type;

With which your Injector becomes:

template <typename Base>
class Injector
{
public:
    typedef std::pair<typename Base::InternalType, 
                      typename Base::InternalType> RefinedType;
    rebind_1st_t<Base, RefinedType> refinedStorage;
};
like image 33
Barry Avatar answered Oct 05 '22 16:10

Barry


There is no need for a rebinding template, that over-complicates the situation. Simply have a template template type:

template<typename>
struct Injector;

template<typename T, template<typename> class Base>
struct Injector<Base<T>>{
    using refined_type = std::pair<typename Base::InternalType, typename Base::InternalType>;
    Base<refined_type> refined_storage;
};

You'll have to use a template specialization to get a concrete type from a template template.

This is used like so:

using injector_int = Injector<Base<int>>;

int main(){
    injector_int i;
}

here's a live example

like image 30
RamblingMad Avatar answered Oct 05 '22 17:10

RamblingMad


You can provide an external rebinder:

template <class Bound, class U>
struct rebinder;

template <template <class> class Binder, class B, class U>
struct rebinder<Binder<B>, U>
{
  typedef Binder<U> type;
};

// Usage:

template <typename Base>
class Injector
{
public:
    typedef std::pair<typename Base::InternalType, typename Base::InternalType> RefinedType;
    typename rebinder<Base, RefinedType>::type refinedStorage;
};

[Live example]

like image 28
Angew is no longer proud of SO Avatar answered Oct 05 '22 16:10

Angew is no longer proud of SO