2

I have a vector of binary data used by some middleware

std::vector<uint> data

The data inside this vector maps to a class Foo basically like that:

|uint|uint|uint|uint|uint|uint|uint|uint|uint|
|    F o o     |    F o o     |    F o o     |

Using a custom placement allocator (as described here Can I use an std::vector as a facade for a pre-allocated (raw) array?) I can now map the memory of the first vector to a vector of type std::vector<Foo>

But if I operate on the second vector the size of the first one is not updated.

Another approach would be to encapsulate the first vector std::vector<uint8> in a custom container which behaves like a std::vector<Foo> but this is a lot of effort (and I cannot use Boost).

Any ideas for an elegant solution?

Andreas Pasternak
  • 1,250
  • 10
  • 18
  • *'But if I operate on the second vector the size of the first one is not updated.'* - not only - if you need to re-allocate because of new elements, other vector won't get updated as well (instead, the link between both gets entirely lost). I assume the middleware is not under your control, is it? – Aconcagua Nov 15 '18 at 00:55
  • Yes, as @Aconcagua says, define 'operate on'. What changes to that vector do you need to make? Or, to take a more holistic view, what does a `std::vector` give you that makes you want to jump through these hoops in order to use it? – Paul Sanders Nov 15 '18 at 00:57
  • What operations do you need to do on `vector`? I'd do something simple like `auto start = reinterpret_cast(vec.data()); auto finish = reinterpret_cast(vec.data() + vec.size());` Then use `start` to `finish` as a range. Write utility functions for things like `push_back`. – Filipp Nov 15 '18 at 01:01
  • @Filipp That's more or less the custom container already mentioned... But actually, it appears to me the most *'elegant'* solution anyway, even if there's quite some effort... – Aconcagua Nov 15 '18 at 01:05
  • @Aconcagua: What I would do to prevent the reallocation is to provide a custom allocator which keeps the memory mapping consistent (see the link). Still the sizes are not synchronized. – Andreas Pasternak Nov 15 '18 at 02:08
  • So you want to be able to remove elements only - or at least never add more elements that initially have been in? – Aconcagua Nov 15 '18 at 02:10
  • @Aconcagua: I can live with the requirement that the capacity of the first vector is fixed. But the allocator could have a reference to the first vector and deduce the maximum size this way. So I have full control over the allocator. But on an allocator level I do not know about the size of the vector using it only about its capacity. – Andreas Pasternak Nov 15 '18 at 02:16

1 Answers1

0

The situation is very different to the linked question. You aren't dealing with preallocated storage, you have data for objects already there. Furthermore, you wish to have both sides be able to modify the vector, or in other words they have shared ownership.

This isn't trivial. You can provide a "view class" that offers another set of operations on the same vector or have a family of free functions do the same. Either way they must have reference semantics.

A free function example

template<typename T>
void push_back_foo(std::vector<uint8_t>& vec, T&& t)
{
    auto it = vec.insert(vec.end(), sizeof(Foo), {});
    new (&*it) Foo{std::forward<T>(t)};
}

And so on for all the other functions. They map operations on the Foo side to operations on the uint8_t side.

A side note: doing pointer arithmetic on the resulting vector is formally undefined behaviour because there is no array of Foo there. When dealing with overlaying binary data with a facade, it is generally understood that compilers won't burn down your house and such formalities are ignored.

Passer By
  • 19,325
  • 6
  • 49
  • 96