tags:

views:

119

answers:

7

I have a situation where I will need an amount of memory determined at runtime to pass to a function. I am using a larger buffer on the stack, then only creating on the heap the space that is necessary:

Foo largeBuf[1024];

int sizeUsed = fillBuff(largeBuf, 1024);

Foo* smallerBuf = new Foo[sizeUsed];

for (UINT i = 0; i < sizeUsed; i++)
{
 smallerBuf[i] = largeBuf[i];
} 

Is there a better way to do this? Some standard array copying function?

+4  A: 

Some standard array copying function?

You mean other than std::copy()?

Noah Roberts
Looks like `std::copy()` is for STL containers. What about raw arrays?
Rosarch
It should work as well because an array is iterateable as far as I remember. Just try it and see
the_drow
@Rosarch: `std::copy` just requires types that support iterator operations. Pointers work there as well. (Specifically, pointers act like random-access iterators.) You'll find a lot of people make utility functions to make it easier to treat automatic arrays as containers (like `endof(arr)` returning a pointer to the end). But there is `boost::/std::array` for the whole she-bang. (Quite a simple class.)
GMan
@Rosarch - incorrect. It works on iterators, not containers. BIG difference.
Noah Roberts
A: 

In C++ you could use std::copy. If your function doesn't modify an array you could pass a const pointer and a size without copying array: some_func( largeBuf, sizeUsed );

Kirill V. Lyadvinsky
+4  A: 

Firstly, you should be using std::vector. There's no reason not to use it. Then use std::copy:

// surely the pointer was an accident
Foo largeBuf[1024];

// int? design should be changed to use an unsigned type
int sizeUsed = fillBuff(largeBuf, 1024); 

// vector, no memory leaks
std::vector<Foo> smallerBuf;
smallerBuf.reserve(sizeUsed);

// copy
std::copy(largeBuf, largeBuf + sizeUsed, std::back_inserter(smallerBuf));

Or just cut to the chase at the end with:

std::vector<Foo> smallerBuf(largeBuf, largeBuf + sizeUsed);

Or another approach:

std::vector<Foo> buf(1024); // this replaces your stack array
buf.resize(fillBuff(&buf[0], 1024)); // copies directly into it, resizes

Note after this last approach, the memory will still be in use. You can force the capacity to shrink with the copy-swap trick:

template <typename T, typename A>
void shrink_to_fit(std::vector<T, A>& pVector)
{
    std::vector<T, A>(pVector).swap(pVector);
}

// ...

shrink_to_fit(buf);

Most ideal, fillBuf would have a way (or another function) to just return the number of elements it will return given a buffer. Then you can just do:

std::vector<Foo> buf(buffSize()); // or whatever
fillBuff(&buf[0], buf.size());
GMan
You need a `back_insert_iterator`, not `smallerBuf.begin()` (unless you meant `resize` instead of `reserve`).
Charles Bailey
@Charles: Nope, I meant `reserve` and just forgot to use `back_inserter`. :) Thanks.
GMan
"There's no reason not to use [a vector]" I believe it is overkill, and `fillBuff()` takes an array, not a vector. Are these objections invalid?
Rosarch
@Rosarch: I doubt you have any valid reason to think it's "overkill". It's part of the language! :) And `vector` is just a safe wrapper around a dynamic array, observe my last two methods for passing an array. It should be noted that neither you or I are passing arrays: we're passing pointers to the first element and the number of elements located at that address. So a dynamic array or automatic array work the same.
GMan
A vector is never overkill... and as other answers state, just use the filled array to populate a vector.
rubenvb
+5  A: 

You should probably be using an std::vector, which you can initialize directly from the elements of the larger buffer:

std::vector<Foo> smallerBuf(largeBuf, largeBuf+sizeUsed);
Jerry Coffin
The method I'm using requires an array. Can a vector be used polymorphically in place of an array?
Rosarch
Eclipse
A: 

I'd think the fastest way would be to use memcpy:

const int BUFSIZE = 1024;

Foo* largeBuf = new Foo[BUFSIZE];

int sizeUsed = fillBuff(largeBuf, BUFSIZE);

Foo* smallerBuf = new Foo[sizeUsed];

memcpy(smallerBuf, largeBuf, sizeUsed * sizeof(Foo));

memcpy_s would be safer if on Windows platform:

memcpy_s(smallerBuf, sizeUsed, largeBuf, sizeUsed * sizeof(Foo));
Avalanchis
It doesn't offer any advantage over `std::copy`, and loses the ability to copy non-POD's. Might as well consistently use `std::copy` throughout the program. (And use `std::vector`!)
GMan
Good to know thanks!
Avalanchis
A: 

One idiom you see a lot is to call fillBuff twice. Once to get the size, and once to to the work. Often it's fairly trivial for fillBuff to calculate the needed buffer size:

int sizeUsed = fillBuff(NULL, 0);
Foo* buffer = new Foo[sizeUsed];
fillBuff(buffer, sizeUsed);

If that's not the case, I'd recommend going with a vector:

vector<Foo> buffer;
fillBuff(buffer);

Or, if you must resort to allocating twice, at the very least, specify the size of your buffer:

Foo largeBuf[1024];
int sizeUsed = fillBuff(largeBuf, 1024);

Foo* smallerBuf = new Foo[sizeUsed];
std::copy(largeBuf, largeBuf + sizeUsed, smallerBuf);
Eclipse
A: 

How often is this going to occur? If this is going to be in a tight loop, there are lots of different options to consider. You could create stack or heap in advance and do some memory-management yourself. Or you could just pass pointers and share the data if you can do so safely without editing it unknowingly.

If this is a very time-sensitive thing, there are many different implementations, though I'd stay away from creating more layers of objects around things if you don't need to. And if you are filling memory with zeroes, check for implementation speed differences between malloc and setting things to 0 vs just calloc. Or system-specific things like bzero() that sometimes make use of hardware-specific memory commands under-the-hood. Also beware memcpy() vs memmove() - determine which is necessary.

For a bit of fun trivia: http://en.wikipedia.org/wiki/Duff%27s_device :-)

eruciform