views:

723

answers:

4

i have a 3d stl vector,

vector<vector<vector<double> > > mdata;

i also have a function

myfun(const double ya[]);

to be more precise, it's a function from the GNU Scientific Library,

gsl_spline_init(gsl_spline * spline, const double xa[], const double ya[], size_t size);

but this is not related to my problem.

so now i want to pass the 'last' dimension of data to myfun. i've been trying this:

for (int s = 0; s < msize; s++) {
 accelerators = new gsl_interp_accel*[msize];
 splines = new gsl_spline*[msize];
 for (int i = 0; i < msize; i++) {
  accelerators[i] = gsl_interp_accel_alloc();
  splines[i] = gsl_spline_alloc(gsl_interp_cspline_periodic, msize+1);
  gsl_spline_init(splines[i], &(*mgrid.begin()), &(*mdata[s][i].begin()), msize+1);
 }
}

But the compiler (g++, 64bit, Ubuntu), complains:

In member function ‘std::vector<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >, std::allocator<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > > > > SimpleAmfCalculator::interp_m(int)’: Calculator.cpp:100: error: cannot convert ‘std::vector<double, std::allocator<double> >*’ to ‘const double*’ for argument ‘3’ to ‘int gsl_spline_init(gsl_spline*, const double*, const double*, size_t)’ make: *** [Calculator.o] Error 1

Any help is greatly apprecitated!

A: 

I think

&(*mdata[s][i].begin());

is returning a std:vector of type double.

jscharf
No, it's returning an iterator. You're thinking of front().
Mark Ransom
No, front returns a double. But it does return an iterator. No function call would return an std::vector of type double.
Walt W
"Calculator.cpp:100: error: cannot convert ‘std::vector<double, std::allocator<double> >*’ to ‘const double*’ for argument ‘3’ "
jscharf
That returns an iterator, which may or may not be compatible with a double* .
Jim Lewis
+7  A: 

You could pass the address of the first element, for example:

#include <vector>

void fun(const double data[])
{

}

int main()
{
    std::vector<std::vector<std::vector<double> > > data3d;
    ....
    fun(&data3d[0][0][0]);
}


The elements of vector are stored contiguously. So this way is standard as I hope :)

23.2.4 Class template vector

1 A vector is a kind of sequence that supports random access iterators. In addition, it supports (amortized) constant time insert and erase operations at the end; insert and erase in the middle take linear time. Storage management is handled automatically, though hints can be given to improve efficiency. The elements of a vector are stored contiguously, meaning that if v is a vector where T is some type other than bool, then it obeys the identity:

&v[n] == &v[0] + n for
all 0 <= n < v.size().
AraK
I think I read once that this used to work in practice or 'de facto' (it assumes that vectors are implemented internally using a contiguous array), and in a newer version of the standard now works in theory or 'de jure' as well.
ChrisW
I'm not sure whether this is portable (vector doesn't provide .data() method like std::string) but this will probably work with most compilers.
nimrodm
It works as long as there is at least one element. And also note that you may only access `data3d[0][0][0].size()` elements, because the other "sub-arrays" (vectors really) are not allocates right next to it (this seems obvious, but easy to overlook in a hurry, i guess).
Johannes Schaub - litb
This fails spectacularly if on of the vectors happens to be empty.
sbi
andreash
This will work for ONE dimension. So assuming [0][0][0] -> x,y,z cordinates. You can access z -> 0 ... n by incrementing the pointer. But you can not get access for any elementas where x != 0 or y != 0.
Martin York
+5  A: 

This cries out for a general solution.

template<typename T, typename A>
T* PointerOf(std::vector<T,A> & vec)
{
    return &vec.at(0);
}

template<typename T, typename A>
const T* ConstPointerOf(const std::vector<T,A> & vec)
{
    return &vec.at(0);
}

myfun(ConstPointerOf(mdata[s][i]));

Edit: I added the template parameter for the vector allocator as suggested in the comments; I also used at() instead of [] so I wouldn't have to check for an empty vector, and I added a second version of the function for a const pointer.

Mark Ransom
Doesn't this need to also provide a template for the allocator?
GMan
@GMan, as written, it will accept only vectors that have a `std::allocator<T>` allocator. I guess that's fine for most. But surely adding the allocator would be a bit more generic :)
Johannes Schaub - litb
You're right, I have never used an allocator in my vectors so it didn't occur to me. Is fixing it just a matter of adding a second template parameter?
Mark Ransom
Why's the `const` version named differently? (Oh, and look at http://stackoverflow.com/questions/1339470/how-to-get-the-address-of-the-stdvector-buffer-start-most-elegantly/1339767#1339767)
sbi
I named the const version so you could choose to return a const or non-const pointer. Since T* will implicitly convert to const T*, I probably didn't need to do that. Still need two versions of the function though.
Mark Ransom
A: 

So, the following seems to work for me:

#include <vector>

void fun(const double data[])
{

}

int main()
{
    std::vector<std::vector<std::vector<double> > > data3d;
    ....
    fun(&(data3d[0][0].front()));
}
andreash