Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to access the underlying container of STL container adaptors?

People also ask

What is the difference between a container and a container adapter?

A container adaptor is not a container at all, since it doesn't implement the interface of a container (doesn't provide an iterator, etc). Instead, it provides limited access to a container. Those provided in the standard library operate using a sequence.

What are the container Adaptors?

Explanation: Container Adaptors is the subset of Containers that provides a different interface for sequential containers.

Are STL containers passed by reference?

@BjörnPollex Yes! I forgot to mention that.

Which of the following are the container adapters in the STL in C++?

Container Adapter These types of containers are called container adapters. The C++ Standard Library implements class templates such as stack, queue, and priority_queue as a container that puts constraints on the process of storage and retrieval of elements.


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 :).


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.


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).


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.