1

So there's a ton of cases where this it needed in performance sensitive applications, and I'm finally at the straw that broke the camels back. It needs to compile in C++98, as at least one of our platform only guarantees C++98 compliance.

Edited hopefully to be a bit more clear with what I want.

Example:

// This will allocate storage for 1024, and then loop 1024 times touching all of it just to place a 0 in it 
std::vector< char > buffer( 1024 );
// Now read will write into that buffer, overwriting the 0s ( we payed for the fill unnecessarily ) 
buffer.resize( read( someSource, &buffer[0], buffer.size() ) );

This is the common C interface, used with nearly all C libraries for writing data to a buffer. The same problems arise when dealing with buffers containing primitives in general. New resize would instead look something like this:

// Disabled for anything which doesn't pass boost::is_POD< T >, they get the standard version
void resize( size_t a_NewSize )
{
     reserve( a_NewSize );
     _end = _begin + a_NewSize;
}

construct_back would be a forwarding constructor, for 1 const argument it would look something like this ( untested ):

template< typename T1 >
void construct_back( const T1& a_Arg1 )
{
    if( capacity() <= size() ) // No room
         reserve( size() + 1 );
    // Construct in place using Ts constructor that accepts const T1&
    new (&(*end()) T( T1 );
    ++_end; // Account for new element
}

construct_back would have to have all the possible number of arguments^2 overloads, this is the common brute force approach for perfect forwarding in C++98.

Ylisar
  • 4,293
  • 21
  • 27
  • is it the simplest way you could ask? – Rohit Vipin Mathews Mar 07 '12 at 09:04
  • @Ylisar I hear you mate, it seems no one has understood your question. I reference this http://stackoverflow.com/questions/1461276/stdvector-reserve-and-push-back-is-faster-than-resize-and-array-index-w to hopefully make it clearer to others. – hiddensunset4 Mar 09 '12 at 02:55

3 Answers3

3

To preallocate memory in a vector use vector::reserve. This will not only work for primitive types but for all types.

To construct elements in place you will need to use C++11 with vector::emplace_back or one of the containers provided by Boost.Containers.

If you need lazy construction (because your constructors are expensive) std::vector<boost::optional> is a good solution.

pmr
  • 58,701
  • 10
  • 113
  • 156
  • 1
    vector::reserve preallocates, but there's no way to use that memory without first increasing the size of the vector, increasing the size means initializing all elements up to size, which in my experience 99% of the time you don't want to do with primitives. emplace_back looks interesting, but sadly I can't use anything not present in C++98, forgot to mention that. – Ylisar Mar 07 '12 at 09:07
  • @Ylisar You should mention this in your question. This requirement is essential.. To me it looks like you are not really looking for a vector but rather a `map`. Also the behavior you specify is not really achievable for anything with a non-trivial copy constructor which makes all this a lot harder. – pmr Mar 07 '12 at 09:10
  • I'll try to rephrase as I've obviously not been very clear. Forwarding construct_back would use placement new, and is fairly trivial to implement unless I've missed something ( although cumbersome in C++98 as one needs to provide all the possible overloads, the usual forwarding limitations in c++98 ). What I want the resize() variant to do is exactly the same as reserve, but at the same time also modify the size ( this will of course give us ripe opportunities for all hell breaking lose if T isn't a primitive, but that can be solved with disable_if ). – Ylisar Mar 07 '12 at 09:24
  • @Ylisar Then you would end up with a container that behaves differently if `is_pod` is true. I don't know if that is really worth it. I'll add a more standard solution for lazy construction. Also, `resize` on a `vector` where `T` is primitive will simply end up as a `memset`. This is really far from expensive. – pmr Mar 07 '12 at 09:31
  • Yeah, perhaps it should be a new function name which is more descriptive and doesn't conflict with old code. Even if it were to be reduced to a memset ( which is not always possible for all Ts, especially wider types ). memset would still need to touch the entire buffer unnecessarily, and then read would touch it starting from the beginning again. For a 1024 buffer a trivial benchmark nets ~6 times faster for not initializing, for a 1024 * 32 buffer it's about ~160 times faster not initializing. boost::optional is a good compromise for std::vector<>, but introduces indirection. – Ylisar Mar 07 '12 at 10:19
1

Your compiler might be clever enough to avoid the initialization. (If it is inlining everything and you only use this vector locally, it isn't too complicated to deduce that the initialization is obsolete.)

Otherwise:

If you care that much about the performance, and if you anyway have a fixed-sized buffer, why not just use an array on the stack? I.e.:

char buffer[1024];
size_t blen = read( someSource, buffer, sizeof(buffer) );

You also avoid the indirection to the heap introduced by std::vector in that case.

You could also build your own template container around that, i.e.:

template<typename T = char, size_t MaxSize = 1024>
struct Buffer {
    static const size_t maxsize = MaxSize;
    typedef T type;
    type data[maxsize];
    size_t len;
    Buffer() : len(0) {}
};

And insert any other functions there as you want (to make it STL-container-like).

Albert
  • 65,406
  • 61
  • 242
  • 386
  • Using a buffer on the stack is often nice, but it has some drawbacks. What I'd ideally want is more or less a drop in replacement for std::vector<>. – Ylisar Mar 09 '12 at 12:21
0
v1.reserve( 200 );

use of reserve can reduce the number of resizing read more about reserve at MSDN

Rohit Vipin Mathews
  • 11,629
  • 15
  • 57
  • 112