Thread: Problem with specializing templates

  1. #1
    The larch
    Join Date
    May 2006
    Posts
    3,573

    Problem with specializing templates

    What I am trying to do is to detect automatically whether a container contains simple value_types or key-value pairs (whether the container is map or similar or not). For that I am hoping to instantiate a specialized template for containers whose value_type is a std::pair).

    However, the pair specialization is not picked up, unless I specifically state the pair with its template arguments. (The int specialization is just to check that it in principle should work.)

    What could I do to make maps automatically detected?
    Code:
    #include <vector>
    #include <list>
    #include <map>
    #include <iostream>
    
    template <class Cont, class ValueType = typename Cont::value_type>
    struct S
    {
        void print() { std::cout << "General\n"; }
    };
    
    template <class Cont>
    struct S <Cont, int >
    {
        void print() { std::cout << "Special: int\n"; }
    };
    
    template <class Cont>
    struct S <Cont, std::pair<typename Cont::key_type, typename Cont::mapped_type> >
    {
        void print() { std::cout << "Special: map\n"; }
    };
    
    int main()
    {
        S<std::list<int> > s1;
        S<std::vector<double> > s2;
        S<std::map<int, double> > s3;
        S<std::map<int, double>, std::pair<int, double> > s4;
        S<std::map<int, double>, std::map<int, double>::value_type > s5;
    
        s1.print(); //Special: int   OK
        s2.print(); //General        OK
        s3.print(); //General        not OK, expected Special: map
        s4.print(); //Special: map   OK
        s5.print(); //General        not OK
    }
    Last edited by anon; 04-02-2008 at 06:13 AM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  2. #2
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Nevermind, I figured it out by checking
    Code:
    std::cout << (typeid(std::map<int, double>::value_type) == typeid(std::pair<int, double>)) << '\n';
    and
    std::cout << (typeid(std::map<int, double>::value_type) == typeid(std::pair<const int, double>)) << '\n';
    The specialization didn't match because the map's key_type is const.

    ---------------------
    Just for the fun, here's a class to help print standard containers' contents.

    Code:
    #include <iostream>
    #include <list>
    #include <vector>
    #include <set>
    #include <map>
    #include <string>
    #include <limits>
    
    template <class Container, class ValueType = typename Container::value_type >
    struct Printer
    {
        void print(std::ostream& os, typename Container::const_iterator it) const
        {
            os << *it;
        }
    };
    
    template <class Container>
    struct Printer <Container, std::pair<const typename Container::key_type, typename Container::mapped_type> >
    {
        void print(std::ostream& os, typename Container::const_iterator it) const
        {
            os << it->first << '=' << it->second;
        }
    };
    
    
    template <class Container>
    class Repr
    {
            const Container& c;
            Printer<Container> printer;
            const std::string separator;
            typename Container::size_type max;
    
        public:
            Repr(const Container& c, const std::string& sep, typename Container::size_type n): c(c), separator(sep), max(n) {}
    
        template <class C>
            friend std::ostream& operator << (std::ostream& os, const Repr<C>& cont);
    };
    
    
    
    template <class Container>
    std::ostream& operator << (std::ostream& os, const Repr<Container>& cont)
    {
        if (cont.max == 0 || cont.c.begin() == cont.c.end()) {
            return os;
        }
        typename Container::size_type counter = 1;
        typename Container::const_iterator it = cont.c.begin();
        cont.printer.print(os, it);
        for (++it; it != cont.c.end() && counter != cont.max; ++it, ++counter) {
            os << cont.separator;
            cont.printer.print(os, it);
        }
        if (it != cont.c.end()) {
            os << cont.separator << "...";
        }
        return os;
    }
    
    template <class Container>
    Repr<Container> make_repr(
        const Container& c,
        const std::string& separator = std::string(" "),
        typename Container::size_type max = std::numeric_limits<typename Container::size_type>::max()
        )
    {
        return Repr<Container>(c, separator, max);
    }
    
    int main()
    {
        std::string data[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven" };
        typedef std::list<std::string> Container;
        Container array(data, data + sizeof(data) / sizeof(data[0]));
        std::cout << make_repr(array) << '\n'           //print using defaults
                  << make_repr(array, ", ", 4) << '\n'  //print first 4 items, using user-defined delimiter
                  << make_repr(array, "\n") << '\n';    //print each item on a new line
    
    
        std::map<std::string, int> Map;
        Map["One"] = 1;
        Map["Two"] = 2;
        Map["Three"] = 3;
        Map["Four"] = 4;
        std::cout << make_repr(Map, ", ") << '\n';    //works for std::map too
    }
    Last edited by anon; 04-02-2008 at 08:09 AM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  3. #3
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Oh, the joy was short-lived. The std::pair<> specialization works with MingW but not with MSVC++ 2005. Am I doing something wrong or is one of the compilers wrong here?

    Another question. If I wanted to move the Repr class into a separate header, how would I know what standard headers I'd need to include? Or, should I include nothing and expect the user to include required standard headers before including this file?

    Sorry about reponding to myself...
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    You could use boost::enable_if, along with a helper template that determines if a type is a map type. The first part requires reading the docs, the second part might look like this:

    Code:
    template <typename T>
    struct is_pair : public false_type {};
    
    template <typename K, typename V>
    struct is_pair<std::pair<K, V> > : public true_type {};
    
    template <typename T>
    struct is_map : public is_pair<typename T::value_type> {};
    
    template <typename T>
    struct is_map<T> : public false_type {};
    That should produce a predicate is_map<> which is suitable for using with boost::enable_if.

    EDIT: Of course, this is not perfect, because all it checks is whether value_type is a std:: pair or not. But that doesn't necessarily mean the container is a map -- it could be a vector of pairs, for instance.
    Last edited by brewbuck; 04-02-2008 at 10:21 AM.

  5. #5
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    You would include the headers you use explicitly. That would be <string> (std::string), <ostream> (std:stream) and <utility> (std:air).
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Laptop Problem
    By Boomba in forum Tech Board
    Replies: 1
    Last Post: 03-07-2006, 06:24 PM
  2. Questions about Templates
    By Shamino in forum C++ Programming
    Replies: 4
    Last Post: 12-18-2005, 12:22 AM
  3. Replies: 5
    Last Post: 11-07-2005, 11:34 PM
  4. Problem with templates
    By Lazy Student in forum C++ Programming
    Replies: 3
    Last Post: 11-17-2002, 12:57 PM