Mohammed Li
Mohammed Li

Reputation: 901

how to write a template of template function acting on nested stl containers and restricting the innermost type

I am currently experimenting with templates of templates of stl containers

template <template <typename, typename > class Container, typename element, typename Allocator>
void printsize (Container<element, Allocator> & a){
    std::cout<<"container size: "<<a.size()<<std::endl;
};


int main(){

    std::vector<double> testvector;

    for(int i = 0; i < 4; ++i){
        testvector.push_back((double) i);
    }

    printsize(testvector);

    std::list<std::vector<double> > testlist;
    for(int i = 0; i < 8; ++i){
        testlist.push_back(testvector);
    }
    printsize(testlist);
}

output correctly is

container size: 4
container size: 8

I am trying to limit the scope of template types the function accepts to something like

stl::container<double>.

If I try something like this:

template <template <typename, typename > class Container, typename Allocator>
void printsize (Container<double , Allocator> & a){
    std::cout<<"container size: "<<a.size()<<std::endl;
};

will make the compiler complain about a

std::vector<int>

container as intended, however it does not work anymore on a

std::list<std::vector< double> >

because, well, double is a different type than std::vector

However, I want my function to act on arbitrary stl containers and stl containers of stl containers ( and so on ) of a specific "base" type only.

The reason behind this is that I do not want to use a lot of overloading. Basically what I am trying to achieve is writing some function

B dosomestuff(A a){
    B b =  someWorkDoneOnA(a);
    return b;
}

stl::container<A> dosomestuff(stl::container<B> myContainer){
    B newContainerElement;
    stl::container<A> outputContainer;
    for(auto i& : myContainer){
         newContainerElement = dosomestuff(i);
         outputContainer.push_back(newContainerElement);
    }
    return outputContainer;
};

By now I have a function that acts on vectors of B and creates vectors of A, overloaded with another function which acts on vectors of vectors of B and outputs vectors of vectors of A (calling the former function) and so on (honestly, for now that's it and I do not think I will need much more, but who knows?). It is literally the same piece of code (except for the type declarations), i.e. WET.

Upvotes: 1

Views: 63

Answers (1)

Caleth
Caleth

Reputation: 63097

Lets start with a type trait for "N-Nested container of B's" (where N == 0 is B)

template <typename T>
struct is_B : std::false_type;

template <template <typename, typename > class Container, typename Element, typename Allocator>
struct is_B<Container<Element, Allocator>> : std::is_B<Element>;

template <>
struct is_B<B> : std::true_type;

You can then augment the working template with enable_if:

template <template <typename, typename > class Container, typename Element, typename Allocator>
void printThings(Container<std::enable_if<is_B<Element>::value, Element>, Allocator>) ...

This gets us halfway. We then make a collection type constructor template

template <typename T>
struct Bs_to_As {};

template <template <typename, typename > class Container, typename Element, typename Allocator>
struct Bs_to_As<Container<Element, Allocator>> { typedef Container<Bs_to_As<Element>::type, Allocator> type; }

template <>
struct Bs_to_As<B> { typedef A type; }

template <template <typename, typename > class Container, typename Element, typename Allocator>
Container<Bs_to_As<Element>, Allocator> doStuff(Container<Element, Allocator> myContainer)
{ ... }

Upvotes: 1

Related Questions