views:

139

answers:

3

If i have a type T, what is a useful way to inspect it at compile time to see whether its an STL-style container (for an arbitrary value type) or not?
(Assumption: pointers, reference, etc. already stripped)

Starting code:

template<class T> // (1)
void f(T&) {} 

template<class T> // (2)
void f(std::vector<T>&) {} 

void test() 
{
    int a;
    std::vector<int> b;
    f(a);
    f(b);
}

Now this works fine, but what if i want to generalize the container (i.e. not define (3), (4), ... explicitly)?

Utilizing SFINAE and typelists would reduce the code somewhat, but is there a better way?
Or is there an idiom for specializing based on concepts?
Or could i somehow utilize SFINAE to selectively enable only the desired specializations?

As a sidenote, i can't use iterators - i am trying to specialize based on functions that receive Ts as parameters.


As per MSalters answer:

template<class T>
void f(T&, ...) {
    std::cout << "flat" << std::endl; 
}

template<class Cont>
void f(Cont& c, typename Cont::iterator begin = Cont().begin(),
                typename Cont::iterator end   = Cont().end()) {
    std::cout << "container" << std::endl; 
}

(The variable argument list is needed to make the first f the least preferred version to solve ambiguity errors)

A: 

According to www.cplusplus.com, the only functions that are common to all STL containers are:

  • A constructor
  • operator=
  • size.

At compile time, you can determine whether these operators exist on your type T.

Chip Uni
I thought of something like that, but it seems like a hack to me and could break too easily.
Georg Fritzsche
+2  A: 

Anything you do is almost certain to be extremely fragile. There's simply no clean dividing line between what is or is not "STL". Even if there was a clean dividing line, it would almost certainly be a really poor basis for such a decision in any case. Just for example, if I write (or use) a re-implementation of std::map that's uses an AVL tree instead of the more common R-B tree, why should it be treated differently from std::map?

In the case of hashed containers, there's a whole progression from the various implementations of hash_map, to the Boost containers, to the TR1 containers, to those that will be included in the standard library in C++ 0x. Depending on how you define "STL", there's a pretty good chance that at least one of those isn't STL and another is, but there's no one point at which it's likely to make sense to treat one differently from another.

I think you should think about the characteristics of the containers, and then try to identify the characteristics that really matter to you.

Jerry Coffin
I wasn't trying to isolate stl containers, hence "stl style", but i am lost how to specialize based on the *characteristics*.
Georg Fritzsche
@gf:Why are you specializing on "stl style"? What divides "stl style" from "not stl style"?
Jerry Coffin
I meant supporting stl style operations. The idea was (may be overkill) to not only specialize on some containers and allow anly container that supports e.g. stl-style back insertion.
Georg Fritzsche
+1  A: 

STLcontainers by definition have a typedef iterator, with 2 methods begin() and end() retruning them. This range is what the container contains. If there's no such range, it's not a container in the STL sense. So I'd sugegst something along the line of (not checked)

template<typename CONTAINER>
void f(CONTAINER& c,
       typename CONTAINER::iterator begin = c.begin(),
       typename CONTAINER::iterator end = c.end())
{ }
MSalters
`bitset` doesn't have an iterator, yet it's an STL container...
Chip Uni
MSalters, good point with the default arguments, thanks. It still has issues though, i'll update the question.
Georg Fritzsche
Chip, `bitset` breaks with most of the behaviour of the containers, so i'd count it out.
Georg Fritzsche