webnoon
webnoon

Reputation: 1015

C++ - pointer array to Vector?

double * values; // instead of this,
std::vector<double> values; // I want this.

An API I'm using provides a result as double* pointer. I want to wrap this with the std::vector<double> type.

Upvotes: 58

Views: 89068

Answers (8)

BuvinJ
BuvinJ

Reputation: 11086

To follow up on @Hamid-Reza-Aarzaghi's Window's answer - that no longer works unfortunately, as vector was redefined in that version of STL again.

I solved the problem (for MSVC) by duplicating the definition of the std:vector, renaming it, and exposed the private member _Mypair publicly. That's that's initial point of entry to get to the rest of the private parts you need to access (in MSVC 2019 at least). I then reinterpret_cast any normal vector pointers into pointers to the custom class, allowing me to achieve the goal.

This is obviously NOT future proof! When upgrading compilers someday, this will more than likely need be rewritten. I DON'T recommend this approach unless you REALLY need a solution for this problem and don't see better alternative.

Btw, I didn't post the source here because it's more than 3k lines! But, you already have the source essentially - simply trace into the vector header on your machine. Then slightly change the names of all the classes (add a prefix or suffix to them). It takes maybe 15 minutes.

Upvotes: 0

Mizux
Mizux

Reputation: 9319

If using C++11, you could use a std::vector<std::reference_wrapper<double>>.

#include <functional> // std::reference_wrapper
#include <vector>

#include <iostream>
#include <numeric> // std::iota
#include <random> // std::mt19937
#include <algorithm> // std::shuffle

int main() {
const int N = 10; // Number of elements in your array
double values[N];
std::iota(values, values+N, -4.0);

std::vector<std::reference_wrapper<double>> v(values, values + N);
std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()});

std::cout << "Contents of the array: ";
for(auto i=0; i < N; ++i) std::cout << values[i] << ' ';
std::cout << '\n';

std::cout << "Contents of the array, shuffled: ";
for(auto i: v) std::cout << i << ' ';
std::cout << '\n';

std::cout << "Change values using the vector shuffled\n";
auto j = 44.;
for(double& i: v) i = ++j;

std::cout << "Contents of the array, shuffled: ";
for(auto i: v) std::cout << i << ' ';
std::cout << '\n';
    
std::cout << "Contents of the array: ";
for(auto i=0; i < N; ++i) std::cout << values[i] << ' ';
std::cout << '\n';
}

Possible output:

Contents of the array: -4 -3 -2 -1 0 1 2 3 4 5 
Contents of the array, shuffled: 1 3 -2 0 -3 5 -4 4 -1 2 
Change values using the vector shuffled
Contents of the array, shuffled: 45 46 47 48 49 50 51 52 53 54 
Contents of the array: 51 49 47 53 48 45 54 46 52 50

pro: zero copy

ref: https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper

Upvotes: 5

Hamid Reza Arzaghi
Hamid Reza Arzaghi

Reputation: 368

Thanks to @Ethereal for the nice solution and to make his/her answer more complete:

that code will not compile in visual c++ (maybe will in GCC) because of differences in the std implementation but with some changes, it will work perfectly.

this code tested in Microsoft Visual C++ (VS2015):

#include <iostream>
#include <vector>

template<typename T> std::vector<T> wrapArrayInVector(T* sourceArray, size_t arraySize) {
    std::vector<T> targetVector;
    std::vector<T>::_Mybase* basePtr{ (std::vector<T>::_Mybase*)((void*)&targetVector) };
    basePtr->_Get_data()._Myfirst = sourceArray;
    basePtr->_Get_data()._Mylast = basePtr->_Get_data()._Myend = basePtr->_Get_data()._Myfirst + arraySize;
    return targetVector;
}

int main() {
    int* tests{ new int[3] };
    tests[0] = 100; tests[1] = 200; tests[2] = 300;
    std::vector<int> targetVector{ wrapArrayInVector(tests, 3) };
    std::cout << std::hex << &tests[0] << ": " << std::dec
    << tests[0] << " " << tests[1] << " " << tests[2] << std::endl;
    std::cout << std::hex << &targetVector[0] << ": " << std::dec
    << targetVector[0] << " " << targetVector[1] << " " << targetVector[2] << std::endl;
    std::cin.get();
}

CAUTION:

but you should be noticed you can wrap an array pointer in std::vector just if that pointer is allocated in heap(for example using new keyword) because std::vector is trying to delete the pointer in its destructor, and if array pointer is allocated on the stack it will cause double delete same memory address and it will cause run time error.

so you must not wrap stack allocated array pointer like this

int tests[3];
tests[0] = 100; tests[1] = 200; tests[2] = 300;
std::vector<int> targetVector = wrapArrayInVector(tests, 3);

Upvotes: 2

Ethereal
Ethereal

Reputation: 2834

Others have suggested that you cannot wrap an array in a vector, but that's simply not true; think about it, a vector has an array as it's underlying data container! I had been attempting this off and on for quite some time before I came up with a workable solution. The caveat is that you have got to zero out the pointers after use in order to avoid double-freeing the memory.

#include <vector>
#include <iostream>

template <class T>
void wrapArrayInVector( T *sourceArray, size_t arraySize, std::vector<T, std::allocator<T> > &targetVector ) {
  typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr =
    (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector);
  vectorPtr->_M_start = sourceArray;
  vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = vectorPtr->_M_start + arraySize;
}

template <class T>
void releaseVectorWrapper( std::vector<T, std::allocator<T> > &targetVector ) {
  typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr =
        (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector);
  vectorPtr->_M_start = vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = NULL;
}

int main() {

  int tests[6] = { 1, 2, 3, 6, 5, 4 };
  std::vector<int> targetVector;
  wrapArrayInVector( tests, 6, targetVector);

  std::cout << std::hex << &tests[0] << ": " << std::dec
            << tests[1] << " " << tests[3] << " " << tests[5] << std::endl;

  std::cout << std::hex << &targetVector[0] << ": " << std::dec
            << targetVector[1] << " " << targetVector[3] << " " << targetVector[5] << std::endl;

  releaseVectorWrapper( targetVector );
}

Alternatively you could just make a class that inherits from vector and nulls out the pointers upon destruction:

template <class T>
class vectorWrapper : public std::vector<T>
{   
public:
  vectorWrapper() {
    this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL;
  }   

  vectorWrapper(T* sourceArray, int arraySize)
  {   
    this->_M_impl _M_start = sourceArray;
    this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize;
  }   

  ~vectorWrapper() {
    this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL;
  }   

  void wrapArray(T* sourceArray, int arraySize)
  {   
    this->_M_impl _M_start = sourceArray;
    this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize;
  }   
};  

Upvotes: 18

Seth Carnegie
Seth Carnegie

Reputation: 75150

You can't wrap an array in a vector in place and expect the vector to operate on that array. The best you can do is give the vector the double* and the number of values, which will have the vector make a copy of every element and put it in itself:

int arrlen = 0;

// pretending my_api takes arrlen by reference and sets it to the length of the array
double* dbl_ptr = my_api(arrlen); 

vector<double> values(dbl_ptr, dbl_ptr + arrlen);

// note that values is *not* using the same memory as dbl_ptr
// so although values[0] == dbl_ptr[0], &values[0] != &dbl_ptr[0]

And also, like Praetorian said, if the API you are using expects you to free the memory after using it, you might be interested in smart pointers. See Praetorian's answer.

Upvotes: 59

Praetorian
Praetorian

Reputation: 109249

The other answers show how to make a copy of the returned array and create a vector, but assuming the API allocates memory for the array and expects the caller to delete it, you may also want to consider sticking the array into a smart pointer and using it as is.

int numValues;
std::unique_ptr<double[]> values( apiFunction( &numValues ) );

You can still copy this into a vector but if you do the above steps you don't have to worry about deleting the returned array.

Upvotes: 7

ChanDon
ChanDon

Reputation: 401

Use vector iterator constructor

std::vector<int> value_vec (value, value + n); //suppose value has n elements

Upvotes: 4

Jacob
Jacob

Reputation: 34621

const int N = 10; // Number of elements in your array
std::vector<double> vec_values(values, values + N);

This will copy the data in values to a std::vector.

Upvotes: 9

Related Questions