17

I have the following case:

T* get_somthing(){
    std::vector<T> vec; //T is trivally-copyable
    //fill vec
    T* temp = new T[vec.size()];
    memcpy(temp, vec.data(), vec.size() * sizeof(T));
    return temp;
}

I want to get rid of the copy process by returning the std::vector::data directly like this:

T* get_somthing(){
    std::vector<T> vec; //T is trivally-copyable
    //fill vec
    return temp.data();
}

However, that is wrong since the data is going to be deleted when vec destructor is called.

So, how can I prevent vec from delete its data? In other word I want some kind of move-idiiom from std::vector to C++ Raw Dynamic Array.

P.S. Changing the design is not an option. Using the std::vector there is mandatory. Returning a pointer to array is also mandatory. Becauese It is a wrapper between two modules. One need vector the other need pointer.

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
  • 14
    Any reason not to return the vector? – juanchopanza Apr 15 '16 at 09:49
  • `static std::vector vec;` is the first thing I thought. – xiaofeng.li Apr 15 '16 at 09:49
  • Do you mean to write `memcpy(data, temp.data, size * sizeof(T));`? – vcp Apr 15 '16 at 09:50
  • 1
    I don't get why you have to use vector, it's in the function implementation, the user is not supposed to know what you are doing. :) – xiaofeng.li Apr 15 '16 at 09:52
  • It is a wrapper between two modules. One need vector the other need pointer – Humam Helfawi Apr 15 '16 at 09:54
  • I believe that's against the very concept of vector. However, you could try allocating some dummy data, then `reinterpret_cast` the vector to some `void*` or `int*` and mess with its internals, but that would make you bound to both architecture and libraries, so that's pretty bad I think. – Tomasz Lewowski Apr 15 '16 at 09:55
  • 1
    @HumamHelfawi Your comment about the two modules should be in the question statement as it really helps understand the context. – fjardon Apr 15 '16 at 09:57
  • 5
    BTW, you might want to take a look at N4359: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4359.pdf proposed for C++17 – Tomasz Lewowski Apr 15 '16 at 09:58
  • @TomaszLewowski This is indeed a good link :) It shows the question is well motivated and a standard solution is needed. – fjardon Apr 15 '16 at 10:00
  • 2
    I suppose this would be possible with custom allocator, but I am not sure if your vector might use one. – marcinj Apr 15 '16 at 10:01
  • 2
    @TomaszLewowski That was shot down in flames in LEWG. – T.C. Apr 15 '16 at 10:11
  • Strictly related question: http://stackoverflow.com/q/26525350/3235496 – manlio Apr 15 '16 at 10:13
  • @T.C. that's what I thought, but I wasn't able to easily find information that it's rejected - are you able to provide a reference ? – Tomasz Lewowski Apr 15 '16 at 10:14
  • 1
    @TomaszLewowski https://issues.isocpp.org/show_bug.cgi?id=81 – T.C. Apr 15 '16 at 10:15
  • How do you know the size of the buffer once you have returned just the pointer? – DarioP Apr 15 '16 at 11:04
  • @HumamHelfawi I added some code to my answer that could be a (hacky) solution if the module that takes the pointer also gives it back. – eerorika Apr 15 '16 at 11:46
  • Once you return the pointer, how does that other module know how to destroy it? It doesn't know how your vector had allocated the memory (assuming that you could release the pointer from vector). – Andre Kostur Apr 15 '16 at 14:46
  • I am actully returning a struct that conatain pointer and size variable but I thought it is not important to mention – Humam Helfawi Apr 15 '16 at 15:24

9 Answers9

19

P.S. Changing the design is not an option. Using the std::vector there is mandatory. Returning a pointer to array is also mandatory.

Changing the design is your best option. I recommend reconsidering this stance.

There is (currently) no way to "steal" the buffer of a vector, so given the (silly††) limitations stated in the question, copying is the way to go.

† Tomasz Lewowski linked a proposal that would change this if it is included in a future standard: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4359.pdf (Edit: as pointed out, it was rejected from c++17)

†† Silly until justified by concrete requirements.


It is a wrapper between two modules. One need vector the other need pointer.

Presumably, the other interface that needs the pointer, delegates the destruction of the buffer to the caller, possibly using some sort of call back like void delete_somthing(T*). Taking ownership without giving it back would have been very bad design, in my opinion.

In case you do have control of the destruction, you can store the vector in a map, and erase the vector, when the pointer is passed for destruction:

std::unordered_map<T*, std::vector<T>> storage;

T* get_somthing(){
    std::vector<T> vec; //T is trivally-copyable
    //fill vec
    T* ptr = vec.data();
    storage[ptr] = std::move(vec);
    return ptr;
}

void delete_somthing(T* ptr){
    storage.erase(ptr);
}
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 11
    I can speak to the rejection of the `vector::release()` proposal. In general (with modern custom allocators), it is not beginner code to correctly dispose of the vector resources (correctly implement `~vector()`). Just calling `delete[]` on that pointer would be incorrect, even if you knew that the `vector` was using `std::allocator`. By the time you correctly build all the machinery needed to deallocate the pointer returned by `vector::release()`, you've reinvented a fair chunk of `vector`. It would be an order of magnitude more difficult to use than `unique_ptr::release()`. – Howard Hinnant Apr 15 '16 at 21:38
  • @HowardHinnant "Just calling delete[] on that pointer would be incorrect, even if you knew that the vector was using std::allocator" This seems so weird to me. Can you provide pointer to where this is explained in more details? – NoSenseEtAl Jan 16 '18 at 05:34
  • @NoSenseEtAl If the vector released its pointer to you, that pointer would point to a memory buffer with the first part holding constructed elements, the latter part holding raw memory. The instructions on how to destruct the elements in the first part are held by the vector's allocator, as are the instructions for deallocating the buffer once it no longer holds any constructed elements. The number of constructed elements is held by the allocator, as is the size of the allocated buffer. – Howard Hinnant Jan 16 '18 at 13:56
  • @HowardHinnant ah that, I was thinking about POD types(I guess there is more fancy term, like is_trivial)... Anyway if you want to present my proposal to add release that requires T of vector to be POD that would be great. ;) Joking aside I feel that standard should say that vector should provide release and adopt methods if the underlying allocator uses malloc(so the users can call free on received buffer). But I guess WG21 would hate exposing method that complicated to "normal" users... And AFAIK there is no precedent for container providing method or not based on T. :/ – NoSenseEtAl Jan 16 '18 at 17:55
14

In C++11 there is no option to release the buffer from the vector.

Such extension to standard was proposed to C++17: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4359.pdf but, as T.C. pointed out, it was rejected: https://issues.isocpp.org/show_bug.cgi?id=81

So no luck in the standard for that. Also a related question was posted that is already answered and explains the same problem: Destroy std::vector without releasing memory

If you are able to mess with either of these libraries you can try custom allocators or other weird stuff (like binding yourself to internal implementation of library and messing with private vector data), but really, don't.

Community
  • 1
  • 1
Tomasz Lewowski
  • 1,935
  • 21
  • 29
6

Below is sample code how to do it with custom allocator. This assumes you can make vector actually use custom allocator. Also allocator uses static variable to control destruction of internal buffer. I have checked under VS2015 and its implementation calls deallocate in ~vector only for internal buffer - i.e. it does not manage any other allocations using this allocator.

This is a hack - and I am not sure what consequences its use might have. Its not thread safe for sure (but could be easily fixed after making allow_dealloc thread local).:

http://coliru.stacked-crooked.com/a/5d969a6934d88064

#include <limits>
#include <vector>
#include <iostream>

template <class T>
class my_alloc {
  std::allocator<T> alloc;
public:
  static bool allow_dealloc;

  typedef T        value_type;
  typedef T*       pointer;
  typedef const T* const_pointer;
  typedef T&       reference;
  typedef const T& const_reference;
  typedef std::size_t    size_type;
  typedef std::ptrdiff_t difference_type;

  pointer allocate(size_type num, const void* = 0) { return alloc.allocate(num);  }
  void deallocate(pointer p, size_type num) { 
      if (allow_dealloc) 
        alloc.deallocate(p, num*sizeof(T));  }


  // Squashed as less important
  template <class U> struct rebind { typedef my_alloc<U> other; };
  pointer address(reference value) const { return &value; }
  const_pointer address(const_reference value) const { return &value; }
  my_alloc() throw() { }
  my_alloc(const my_alloc&) throw() { }
  template <class U> my_alloc(const my_alloc<U>&) throw() { }
  ~my_alloc() throw() { }
  size_type max_size() const throw() { return (std::numeric_limits<size_t>::max)() / sizeof(T); }  
  void construct(pointer p, const T& value) { alloc.construct(p, value); }
  void destroy(pointer p) { p->~T(); }  
};

template <typename T>
bool my_alloc<T>::allow_dealloc = true;

int main()
{
  int* data = 0;
  size_t size = 0;
  {
    my_alloc<int>::allow_dealloc = true;      
    std::vector<int, my_alloc<int>> vec= { 0, 1, 2, 3 };
    vec.push_back(4);
    vec.push_back(5);
    vec.push_back(6);
    my_alloc<int>::allow_dealloc = false;
    data = vec.data();
    size = vec.size();
  }

  for (size_t n = 0; n < size; ++n)
    std::cout << data[n] << "\n";

  my_alloc<int> alloc; 
  alloc.deallocate(data, size);
}
marcinj
  • 48,511
  • 9
  • 79
  • 100
4

If there is an option to use smart pointers I would recommend std::shared_ptr with alias:

template<typename T>
std::shared_ptr<T> get_somthing(){
    using Vector = std::vector<T>;
    using ReturnT = std::shared_ptr<T>;
    std::vector<T>* vec = new std::vector<T>;
    //fill vec
    std::shared_ptr<Vector> vectorPtr(vec); // (1)
    std::shared_ptr<T> aliasedPtr(vectorPtr, vec->data()); // (2)
    return aliasedPtr;
}

(1) will create a shared pointer to the vector to be aliased to (2) creates a shared pointer that will destroy the aliased shared_ptr instead of removing contained data

Teivaz
  • 5,462
  • 4
  • 37
  • 75
3

I don't know if you will like this very hacky solution, I definitely would not use it in production code, but please consider:

#include <iostream>
using namespace std;

#include <vector>

template<class T>
T* get_somthing(){
    std::vector<T> vec = {1,2,3}; //T is trivally-copyable

    static std::vector<T> static_vector = std::move(vec);

    return static_vector.data();
}  

int main() {
    int * is = get_somthing<int>();
    std::cout << is[0] << " " << is[1] << " " << is[2];
    return 0;
}

so, as you can see inside the get_somthing I define a static vector, of same type as you need, and use std::move on it, and return it's data(). It achieves what you want, but this is dangerous code, so please use the good old fashioned copy the data again methodology and let's wait till N4359 gets into the mainstream compilers.

Live demo at: http://ideone.com/3XaSME

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • N4359 won't make it to mainstream compilers (was rejected) - see my edited answer and comments under the question – Tomasz Lewowski Apr 15 '16 at 10:20
  • Aren't static local variables only initialized the very first time the function is called? That is, all calls to `get_somthing()` would yield the same data (even if `vec` was obtaining variable data from somewhere). – Peter - Reinstate Monica Apr 15 '16 at 10:41
  • @PeterA.Schneider You're right, but we can write the code like: `static std::vector static_vector;static_vector = std::move(vec);` in this case – Ferenc Deak Apr 15 '16 at 10:52
2

Edit: This idea doesn't work because there is no way to prevent the implicit call to the destructors of base classes (thanks, molbdnilo). That they are called is, if I think of it, a good thing.


I was not entirely sure whether this was viable (and curious as to what others say), but would it be possible to inherit from vector and override its destructor (to do nothing)? Even if ~vector() is not virtual (is there a requirement in the standard for being or not being virtual?) this should work as long as you explicitly use your type.

By inheriting you would retain all the benefits, in particular the memory management -- except for the final bit (which you don't want).

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
2

Isn't the way to go here simply to allocate the vector dynamically? That's how resources with a life time which is unrelated to scope are traditionally managed, and I don't see any reason to invent something extraordinary.

Of course that vector should be destroyed some time later; that may make it necessary to store its address somewhere as a side effect of get_somthing(), but even then this strategy seems cleaner than any of the other ideas.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • That would make a terrible mess. – Lightness Races in Orbit Apr 15 '16 at 14:55
  • @LightnessRacesinOrbit None of the other solutions appear prettier to me. user207..'s map is the same idea as this one: Have a side effect and delete later. One other suggestion, a custom allocator, seems too much effort and too little compatibility. The shared_ptr suggestion simply doesn't meet the requirements (differs in function signature). – Peter - Reinstate Monica Apr 15 '16 at 15:29
  • Dynamically allocating a container is bad, and a function that dynamically allocates anything then returns a raw pointer to it is also bad. Just return the vector and let the move constructor handle resources for you. If you can't change the function signature, then simply don't use a vector. – Lightness Races in Orbit Apr 15 '16 at 15:52
2

The first thing you should do is to get up, go to the one responsible for this design and (verbally in a professional manner) punch him/her in the face: It's a mess.

Then, there's a way in C++11 to have a std::vector with automatic storage duration and have it not call its destructor:

Put the std::vector into an union

Like so:

template<typename T>
union Ugly {
  std::vector<T> vec;
  Ugly() {
    new (&vec) std::vector<T>(); // Construct
  }
  ~Ugly() {
   // Don't destruct
  }
};

T* get_something(){
  Ugly mess;
  //fill mess.vec
  return mess.vec.data();
}

I'm not 100% sure whether this still counts as valid C++11, but it should "work". Now excuse me, I need to wash my hands to get rid of the crying feeling of shame for this code ...

Oh, and one more thing: How do you intend to release the memory that the std::vector had allocated? You know, you can't (reliably) use the pointer returned by the member function data() for that!

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
2

Ok, don't try this at home, it's not nice. It's more an experiment than an actual answer.

(Well, the requirements/design is not nice either: "Play stupid games, win stupid prizes")

in your cpp:

#define private public               // good luck for the code review
#define protected public
#include <vector>                    // (must be the first occurence in the TU)
#undef private                       // do not abuse good things...
#undef protected

template<typename T>
T* my_release(std::vector<T>& v){
    std::vector<T> x;                // x: the local vector with which we mess around
    std::swap(x, v);                 // the given vector is in an OK, empty state now.
    T* out = x._M_impl._M_start;     // first, get the pointer you want

    // x will be destructed at the next '}'. 
    // The dtr only use _M_start and _M_finish, make sure it won't do anything.
    x._M_impl._M_start = nullptr;    
    x._M_impl._M_finish = nullptr;

    // no need to say, the internal state of 'x' is bad, like really bad...
    // also we loose the capacity information, the actual allocator... 
    // -> good luck with memory leaks...

    return out;
}

// usage example
int main(){
    std::vector<int> vi{1,2,3,4,5,6,7,8,9};
    auto n = vi.size();
    int* pi = release(vi);
    for(size_t i=0; i<n; ++i)
        std::cout << pi[i] << ", ";

    return 0;
}

prints 1, 2, 3, 4, 5, 6, 7, 8, 9,

johan d
  • 2,798
  • 18
  • 26