7

My question is simple: Its possible to obtain a pointer to the underlying storage of a std::queue container adapter?

I'm working on some simulations using SFML for rendering, and I use the draw() method of the SFML render target (sf::RenderTarget) to draw the entire bunch of data. That method has a C-like interface expecting a pointer to the data and a std::size_t with the number of elements to draw.

Since the data is stored in a queue for some purposes, I will be glad if there is some way to get that pointer to the queue underlying storage instead of copying the data to a vector.
I know that std::queue adapts the container std::deque by default, but I don't know how that circular buffer is implemented and if its data is contiguous (So I can extract a pointer to the data directly).

EDIT: Performance

Looking at the answers bellow, let me note that I'm not using std::deque because its fancy queue-like interface, but because I really need fast queueing. Of course I can use std::vector. If performance wasn't the point here I would have used push_back()and erase( begin() ) of vector. But what I need is fast queueing and a way to move the data of that queue to the render target efficiently. Of course if the balance queueing vs effort to draw it goes to the draw side, I will use std::vector.

Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • 2
    IIRC, `std::deque` does not have contiguous storage, but is allocated typically in blocks, with an array of pointers pointing to those blocks. – dyp Jul 05 '14 at 19:06
  • No, there is no such member, with reason behind it. The storage provided by the default back-end container, `std::deque<>`, is not mandated by the standard as contiguous. Nor is the implementation mandated as circular, which you also mentioned. – WhozCraig Jul 05 '14 at 19:23
  • 1
    And if you are curious how a `std::deque<>` is typically implemented, [see this answer to a different question](http://stackoverflow.com/a/6292437/1322972) for a common implementation. It spells out nicely why what you seek with a `std::deque<>` back-end simply doesn't exist. – WhozCraig Jul 05 '14 at 19:26
  • No one noticed that `std::queue` just wraps the underlying container, so it calls `container.pop_front()` and then it cannot be used with `std::vector`? (n.m. quickly deleted his answer ;) ). If it was that easy, I would currently using `std::vector` and I wouldn't ever asked a question here (Since asking here is the last thing we should do. I ask on SO when I really have no idea about how to continue). – Manu343726 Jul 05 '14 at 19:34
  • 3
    Honestly, depending on the size of the data you're buffering up for that C API call, I think it likely your copy-then-call approach may very well be your best bet if you truly want fast queuing *and* contiguous data for the call. It at-least-seems to me you would be better off just cutting out the middle man (the queue adapter) use a `deque` straight-way, and keeping your copy-then-call approach. Worthy of benchmarking, I suppose. How much data are we talking about here (per copy)? iterator-based copying (something queue does *not* give but deque *does*), may help significantly. – WhozCraig Jul 05 '14 at 19:34
  • I have the same dilemma and an alternative. If you want to avoid copies, you can perhaps detect the queue block at runtime, and render the blocks one by one. It is ugly but I think it is always better than copying. The performance gain will depend on the size of element relative to the size of pointer. Also there is hope that a future C++ will include the concept of a contiguos iterator and memory blocks. See here https://stackoverflow.com/q/42851957/225186 – alfC Jun 21 '17 at 18:00

2 Answers2

10

Is it possible to obtain a pointer to the underlying storage of a std::queue container adapter?

Short answer: No.

std::queue requires a SequenceContainer type, like std::deque or std::list. Neither of these guarantee contiguous storage, so there's no concept of a pointer to the underlying storage.

If they used contiguous circular buffers, this would either impose a fixed size on the container, or incur large cost (like std::vector can) when the container needs to be resized.

You could look at using a boost::circular_buffer instead.

Roddy
  • 66,617
  • 42
  • 165
  • 277
1

The most intuitive thought would be to use a std::vector as the underling container of your std::queue to assure contiguous memory and then by taking the address of std::queue::front to access the contiguous storage of the wrapped std::vector.

However, as @Blastfurnace correctly noted std::vector doesn't have a pop_front member function and when you were going to call std::queue::pop the program would blast.

Also as already mentioned, std::queue normally requires as its underlying container, a container like std::deque or std::list. Due to their structural traits these containers are the most suitable because they can pop efficiently their front element. However, due to its contiguous memory std::vector lacks of such versatility.

Also, if use of a container with contiguous memory is something that would boost significantly your application it's better and more straightforward to use a std::vector.

However, and for the record, you could also do the following hack in order to circumvent the lack of std::vector's pop_front member function. You could define a class that inherits from std::vector and implement the pop_front member function like the example below:

#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>

template<class T, class Alloc = std::allocator<T>>
class queuevec : public std::vector<T, Alloc> {
public:
  void pop_front() {
   if(!this->empty()) this->erase(this->begin());
  }
};

int main() { 
  std::queue<int, queuevec<int>> Q;

  for(int i(0); i < 10; ++i) Q.push(i);

  Q.pop();

  int *t = &(Q.front());
  std::for_each(t, t + Q.size(), [](int const i){ std::cout << i << " "; });
  std::cout << std::endl;
}

LIVE DEMO

Community
  • 1
  • 1
101010
  • 41,839
  • 11
  • 94
  • 168
  • While that will work, the problem is that the performance of pop_front will be terrible. Even in C++11 there will be a slew of memory moving going on. – Roddy Jul 06 '14 at 11:30
  • @Roddy, I totally agree with you. IMHO, I think that I cover this aspect in my answer by suggesting the OP to use contiguous memory containers only if there's something significant to gain, and of course always after profiling her/his program. – 101010 Jul 06 '14 at 11:53