1

I'm building a buffer for network connections where you can explicitly allocate memory or you can supply it on your own via some sequential container(eg.:std::vector,std::array)these memory chunks are stored in a list what we use later for read/write operations. (the chunks are needed for handle multiple read/write requests) I have a question about the last part, I want to make a pointer to the container's data and then tell the container to not care about it's data anymore. So something like move semantics.

std::vector<int> v = {9,8,7,6,5,4,3,2,1,0};
std::vector<int> _v(std::move(v));

Where _v has all the values of v and v left in a safe state.

The problem is if I just make a pointer for v.data() after the lifetime of the container ends, the data pointed by the pointer releases with the container. For example:

// I would use span to make sure It's a sequential container 
// but for simplicity i use a raw pointer
// gsl::span<int> s;
int *p;
{
   std::vector<int> v = {9,8,7,6,5,4,3,2,1,0};
   // s = gsl::make_span(v);
   p = v.data();
}

for(int i = 0; i < 10; ++i) 
    std::cout << p[i] << " ";

std::cout << std::endl;

Now p contains some memory trash and i would need the memory previously owned by the vector.

I also tried v.data() = nullptr but v.data() is rvalue so it's not possible to assign it. Do you have any suggestions, or is this possible?

edit.: To make it more clear what i'm trying to achieve:

class readbuf_type
{
    struct item_type // representation of a chunk
    {
        uint8_t * const data;
        size_t size;

        inline item_type(size_t psize)
            : size(psize)
            , data(new uint8_t[psize])
        {}

        template <std::ptrdiff_t tExtent = gsl::dynamic_extent>
        inline item_type(gsl::span<uint8_t,tExtent> s)
            : size(s.size())
            , data(s.data())
        {}

        inline ~item_type()
        { delete[] data; }
    };

    std::list<item_type> queue; // contains the memory
public:

    inline size_t read(uint8_t *buffer, size_t size); // read from queue

    inline size_t write(const uint8_t *buffer, size_t size); // write to queue

    inline void *get_chunk(size_t size)
    {   
        queue.emplace_back(size);
        return queue.back().data;
    }

    template <std::ptrdiff_t tExtent = gsl::dynamic_extent>
    inline void put_chunk(gsl::span<uint8_t,tExtent> arr)
    { 
        queue.emplace_back(arr);
    }
} readbuf;

I have the get_chunkfunction what basically just allocates memory with the size, and I have put_chunk what I'm struggling with, the reason i need this because before you can write to this queue you need to allocate memory and then copy all the elements from the buffer(vector,array) you're trying to write from to the queue. Something like:

std::vector<int> v = {9,8,7,6,5,4,3,2,1,0};
// instead of this
readbuf.get_chunk(v.size);
readbuf.write(v.data(), v.size());

// we want this
readbuf.put_chunk({v});

Since we're developing for distributed systems memory is crucial and that's why we want to avoid the unnecessary allocation, copying.

ps.This is my first post, so sorry if i wasn't precise in the first place..

20
  • 7
    It's not possible with standard containers, but why would you do that in the first place? Commented Feb 19, 2019 at 16:23
  • 3
    I'm not sure what problem you are trying to overcome exactly (I don't see how the code relates to the rest of your question). But I think it might help you to know that, barring allocator incompatibilities, a pointer to v.data(); will point to v_.data(); if you move v into v_. Commented Feb 19, 2019 at 16:24
  • 3
    @marko1777 It's not possible to "release" a vector's data to a raw pointer like that as far as I know. Using p after v's lifetime ends is undefined behavior, though I don't doubt that this UB exhibited itself as producing the result you expect (appeared to work). This won't last, any change to your code or the context it runs in could change the behavior. Commented Feb 19, 2019 at 16:30
  • 2
    This is correct. This is not doable with standard containers per se. You will have to implement your own custom containers. The custom container can be nothing more than a single struct with a unique_ptr to the std::vector with the real data, and the "not caring any more" consists of nothing more than moving the unique_ptr somewhere else. Commented Feb 19, 2019 at 16:33
  • 4
    Change int *p; to be a std::vector<int> instead of a int*. Then use p = std::move(v); instead of p = v.data();. Commented Feb 19, 2019 at 16:38

4 Answers 4

4

No, it is not possible to "steal" the buffer of the standard vector in the manner that you suggest - or any other standard container for that matter.

You've already shown one solution: Move the buffer into another vector, instead of merely taking the address (or another non-owning reference) of the buffer. Moving from the vector transfers the ownership of the internal buffer.

It would be possible to implement such custom vector class, whose buffer could be stolen, but there is a reason why vector doesn't make it possible. It can be quite difficult to prove the correctness of your program if you release resources willy-nilly. Have you considered how to prevent the data from leaking? The solution above is much simpler and easier to verify for correctness.

Another approach is to re-structure your program in such way that no references to the data of your container outlive the container itself (or any invalidating operation).

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your answer! I updated the question. Sadly there is no option to organize the code that way..
If you really want to do something like this instead of implementing a custom container, you can just implement a custom allocator what is the same as the standard one, but on deallocate it doesn't free the memory. I don't get the point why would leaking be a problem, because you deallocate the memory in the destructor when you destroy the object what contains it.
1

Unfortunately the memory area of the vector cannot be detached from the std::vector object. The memory area can be deleted even if you insert some data to the std::vector object. Therefore use of this memory area later is not safe, unless you are sure that this particular std::vector object exists and is not modified.

The solution to this problem is to allocate a new memory area and copy the content of the vector to this newly allocated memory area. The newly allocated memory area can be safely accessed without worrying about the state of the std::vector object.

std::vector<int> v = {1, 2, 3, 4};
int* p = new int[v.size()];
memcpy(p, v.data(), sizeof(int) * v.size());

Don't forget to delete the memory area after you are finished using this memory area.

delete [] p;

1 Comment

Thanks for the reply, this would be a great idea but we should minimize the allocations. I edited my question so maybe it's more clear why I need this.
0

Your mistake is in thinking that the pointer "contains" memory. It doesn't contain anything, trash or ints or otherwise. It is a pointer. It points to stuff. You have deleted that stuff and not transferred it anywhere else, so it can't work any more.

In general, you will need a container to put this information in, be it another vector, or even your own hand-made array. Just having a pointer to data does not mean you have data.

Furthermore, since it is impossible to ask a vector to relinquish its buffer to a non-vector thing, a vector is really your only chance in this particular case. It's not quite clear why that's not a good enough solution for you. :)

Comments

0

Not sure what you try to achieve but I would use moving semantic like this:

#include <iostream>
#include <memory>
#include <vector>

int main() {
std::unique_ptr<std::vector<int>> p; 
{
   std::vector<int> v = {9,8,7,6,5,4,3,2,1,0};
   p = std::move(make_unique<std::vector<int>>(v));
}

for(int i = 0; i < 10; ++i) 
   std::cout << (*p)[i] << " ";

std::cout << std::endl;


return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.