33

Is there a standard way to access the underlying container of stack, queue, priority_queue ?

I found a method called : _Get_container() in VS2008 implementation of stack and queue, but no one for priority_queue! I think it is not standard anyway.

Also, I know it is a silly question! where can I find official documentation of the standard library ?


Just for clarification, I wasn't trying to mess up with the underlying container. All what I was trying to do is this :

template <class Container>
std::ostream& printOneValueContainer(std::ostream& outputstream, Container& container)
{
    Container::const_iterator beg = container.begin();

    outputstream << "[";

    while(beg != container.end())
    {
        outputstream << " " << *beg++;
    }

    outputstream << " ]";

    return outputstream;
}

// stack, queue
template
    < class Type
    , template<class Type, class Container = std::deque<Type> > class Adapter
    >
std::ostream& operator<<(std::ostream& outputstream, const Adapter<Type>& adapter)
{
    return printOneValueContainer(outputstream, adapter._Get_container());
}
.
.
.
std::stack<int> iStack;
.
.
std::cout << iStack << std::endl;

I hope you see that _Get_container() is not standard, and there is no one for priority_queue in VS2008 implementation.

Khaled Alshaya
  • 94,250
  • 39
  • 176
  • 234
  • not exactly what you need, but queue/stack/priority_queue all have a **protected** member `c` which is the underlying container, so if you inherit from any of these, you can access it directly. – Evan Teran Jul 27 '09 at 01:05
  • @Evan Interesting! Does this mean that the adaptors are intended for extension via inheritance? If so, why no virtual dtor? –  Jul 27 '09 at 09:21
  • Also, protected data is a no-no in my book - I'm a bit disappointed by this! –  Jul 27 '09 at 09:30
  • well, (as you very likely know) those "containers" are simply adapters which provide a specific interface to an underlying container. I don't know the specific mentality, but I guess, they just wanted to make it so you could "extend" the typical interface to add things such as iteration and similar. There is still no virtual dtor or members. – Evan Teran Jul 27 '09 at 15:11
  • @Neil: I just posted an answer which uses this technique to do what the OP wanted. I'm with you on the protected == bad. Would have been nice if they had a "base" member function in all adapters you things like this could be free functions. – Evan Teran Jul 27 '09 at 15:36

10 Answers10

26

I spotted the following solution somewhere on the web and I'm using it in my projects:

template <class T, class S, class C>
    S& Container(priority_queue<T, S, C>& q) {
        struct HackedQueue : private priority_queue<T, S, C> {
            static S& Container(priority_queue<T, S, C>& q) {
                return q.*&HackedQueue::c;
            }
        };
    return HackedQueue::Container(q);
}

int main()
{
    priority_queue<SomeClass> pq;
    vector<SomeClass> &tasks = Container(pq);
    return 0;
}

Have fun :).

16

Based on the accepted answer, a more general approach:

template <class ADAPTER>
typename ADAPTER::container_type & get_container (ADAPTER &a)
{
    struct hack : ADAPTER {
        static typename ADAPTER::container_type & get (ADAPTER &a) {
            return a.*&hack::c;
        }
    };
    return hack::get(a);
}

As I learned from this answer, .*& is actually two operators, where the pointer resulting from &hack::c (which has type ADAPTER::container_type ADAPTER::*) is the target or the .* operator to retrieve the underlying container itself. hack has access to the protected member, but after the pointer is obtained, protections are lost. So a.*(&hack::c) is allowed.

Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
12

I mentioned it in a comment, but after some thinking, it seems to be an OK solution. queue/stack/priority_queue (that is, all of the adapter classes) all have a protected member c which is the underlying container (see ISO/IEC 14882:2003 section 23.2.2.4), so if you inherit from any of these, you can access it directly.

I know the typical wisdom is to not inherit from STL containers due to non-virtual dtors, but this case is an exception. The goal is not to overload functionality, but to make minor extensions to the interface of the adapter. Here is an example of adding the ability to access the underlying container.

#include <queue>
#include <iostream>

template <class Container>
class Adapter : public Container {
public:
    typedef typename Container::container_type container_type;
    container_type &get_container() { return this->c; }
};

int main() {
    typedef std::queue<int> C;
    typedef Adapter<C> Container;

    Container adapter;

    for(int i = 0; i < 10; ++i) {
        adapter.push(i);
    }

    Container::container_type &c = adapter.get_container();
    for(Container::container_type::iterator it = c.begin(); it != c.end(); ++it) {
        std::cout << *it << std::endl;
    }
}

Unfortunately, you'll have to resort to type-punning to "upgrade" an existing std::queue<int> * to a Adapter<std::queue<int> > *. Technically, this would likely work fine... but I recommend against it:

    typedef std::stack<int> C;
    typedef Adapter<C> Container;
    C stack;
    // put stuff in stack
    Container *adapter = reinterpret_cast<Container *>(&stack);
    Container::container_type &c = adapter->get_container();
    // from here, same as above        

So I would recommend using typedefs to make it easy to swap between the two. (Also notice in my example that you only need to change 1 line to change it from a queue to a stack because of the liberal use of typedefs).

Evan Teran
  • 87,561
  • 32
  • 179
  • 238
3

No, there is no standard way of doing that. As for access to the standard, it is not available onn the web, you have to buy a copy! However, there are various copies of drafts available here.

3

This SGI page is the most "official" documentation available online, I believe.

The reason that you can't get direct access to the underlying container is that the adapter changes the usage pattern, and having the methods of the underlying container available would violate that new usage pattern. For example:

2 This restriction is the only reason for queue to exist at all. Any container that is both a front insertion sequence and a back insertion sequence can be used as a queue; deque, for example, has member functions front, back, push_front, push_back, pop_front, and pop_back The only reason to use the container adaptor queue instead of the container deque is to make it clear that you are performing only queue operations, and no other operations. http://www.sgi.com/tech/stl/queue.html

If you want to get around this design feature, you can do something like:

template <typename T>
class clearable_queue : public std::queue<T>
{
public:
    void clear() { c.clear(); }
};
Tim Sylvester
  • 22,897
  • 2
  • 80
  • 94
  • Usually you should compose containers rather than derive from them. They don't have virtual destructors, for example. – GManNickG Jul 26 '09 at 19:50
  • 2
    Agreed. However in this case we don't care about destructors, and by using inheritance we avoid having to define the `queue` interface as proxy methods. – Tim Sylvester Jul 26 '09 at 23:42
2

As a general rule, any identifier that starts with an underscore is a vendor-specific extension or an implementation detail. So _Get_container() is just an addition made by Microsoft because it simplified their implementation. It is not intended to be used.

As for where to find the documentation, it's split into several parts.

The authoritative source is, of course, the language standard. As Neil Butterworth said, there are draft copies available for free online (which are still very useful. The differences from those to the final version are really minimal). Alternatively, you can buy a copy. It should be available from whichever organization represents ISO in your country (and probably from a bajillion other sources as well). The document you're looking for is ISO/IEC 14882:2003 Programming Language C++. (14882 is the standard number. 2003 is the year of the last revision. If you come across the 1998 version, you can use that too. The differences are really ridiculously small between the two, and basically just amounts to a few clarifications. It's probably best to stay away from the drafts for C++0x, as the changes there are far more extensive)

Apart from that, every implementation of the standard library is required to document a large number of details (implementation-defined behavior, things which are not specified in the standard, but is left up to the library implementer). And in addition, most of them also put up a detailed documentation of the entire library as well.

Microsoft has detailed documentation available on MSDN. The documentation respects the standard, and clearly marks all nonstandard extensions so you know which is which.

SGI also has the documentation online (although it is older and in some cases, not entirely accurate).

IBM has similar documentation available on their website, and I believe GCC does too.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • Oh, didn't even realize this was your question. Thought you knew this stuff already. ;) – jalf Jul 26 '09 at 19:38
1

I certainly hope there isn't a way to access the underlying container of a priority queue. If you could then you could mess up the internal heap structure of the priority queue. In any case the point of those adaptors is that they only present you with the minimal interface of the stack or queue, and abstract out all the other things. So if you needed to use any other features, you should have used the original container directly.

As for documentation of the STL, you can look at the documentation of SGI's STL here. There are a few differences between SGI's STL and the one in the C++ standard, but they are mostly noted on that site. Also, cppreference.com is a wiki documentation of the C++ library that is becoming more complete.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • I see your point. What I am trying to do is writing a generic function that streams the contents of the stack, queue and priority_queue. It is just for fun so no problem :) – Khaled Alshaya Jul 26 '09 at 19:11
0

You can write a subclass to retrieve the member variable c. See this comment from libstdc++.

protected:
/**
 *  'c' is the underlying container.  Maintainers wondering why
 *  this isn't uglified as per style guidelines should note that
 *  this name is specified in the standard, [23.2.3.1].  (Why?
 *  Presumably for the same reason that it's protected instead
 *  of private: to allow derivation.  But none of the other
 *  containers allow for derivation.  Odd.)
 */
_Sequence c;
yufanyufan
  • 171
  • 1
  • 7
0

This should work on recent ISO compilers:

template <typename T, typename Seq = std::vector<T>,
            typename Cmp = std::less<typename Seq::value_type>>
  class PriorityQueueContainer : public std::priority_queue<T, Seq, Cmp> {
   public:
    using base_t = std::priority_queue<T, Seq, Cmp>;
    PriorityQueueContainer() = default;
    template <typename Cont>
    PriorityQueueContainer(Cont&& cont) : base_t(Cmp{}, std::move(cont)) {}
    template <typename Cmp, typename Cont>
    PriorityQueueContainer(const Cmp& cmp, Cont&& cont) : base_t(cmp, std::move(cont)) {}
    template <typename Cont>
    PriorityQueueContainer(const Cont& cont) : base_t(Cmp{}, cont) {}

    static Seq&& retrieve_container(PriorityQueueContainer&& pq) {
      return std::move(pq.c);
    }
  };

Usage:

 PriorityQueueContainer<int> pq();
 std::vector<int> retrieve = PriorityQueueContainer<int>::retrieve_container(std::move(pq));
lurscher
  • 25,930
  • 29
  • 122
  • 185
-2

where can I find official documentation of the standard library?

The C++ standard is available in hardcover, ISBN 0470846747. Drafts of Standard are widely available in PDF form, but you'll have to be careful to match them with the official versions (C++98,03,11 or 14). The current drafts exceed the C++14 standard.

MSalters
  • 173,980
  • 10
  • 155
  • 350