tags:

views:

807

answers:

10

I want to use std::vector for dynamically allocating memory. The scenario is:

int neededLength = computeLength(); // some logic here

// this will allocate the buffer     
std::vector<TCHAR> buffer( neededLength );

// call a function that accepts TCHAR* and the number of elements
callFunction( &(buffer[0]), buffer.size() );

The code above works, but this &(buffer[0]) looks ugly. Is there a more elegant way to achieve the same?

+3  A: 

Try &(buffer.front()), but it's not much prettier :)

Michael Krelin - hacker
This has the disadvantage that it won't work with plain array types.
Richard Corden
It doesn't matter, the question was about `std::vector`. I think the fact that it's not prettier is much worse ;-)
Michael Krelin - hacker
+9  A: 

but this &(buffer[0]) looks ugly

It’s the normal way. You can omit the parentheses, though:

&buffer[0]
Konrad Rudolph
+3  A: 

No.

Pavel Shved
+1 Brevity is the soul of wit!
Jim Lewis
Brevity avails to nothing if it conveys a wrong answer. Both Neil and I have posted simpler solutions (with a whopping 17% character count reduction).
Konrad Rudolph
"Fewer keystrokes" does not necessarily equate to "simpler" or "more elegant" for me. "Clarity" comes into play , especially in a language like C++ where tiny syntactic changes can have surprising consequences. Why make the reader stop and scratch their head over operator precedence, when an extra set of parens can make the intent crystal-clear? I'm still with Pavel on this one!
Jim Lewis
By renaming buffer to just b you get an even lower character count!
graham.reeds
+1 straight to the point with no bs! LOL
markh44
@Jim: how do parentheses help here? They don’t, or rather, they shouldn’t: If you’re unclear about precedence in this simple case, that’s a problem. Parentheses are great in complex expressions but not for these simple cases, where there’s a simple, unambiguous rule that you *must* know as a C++ programmer. And yes, brevity does not equal clarity but as Pavel himself has demonstrated, it can go a long way towards achieving it.
Konrad Rudolph
I think that mixing prefix and postfix operations where postfix one (operator []) _preceedes_ the prefix one (operator what makes me deserving less rep--I must consider that others may have different tastes.
Pavel Shved
Speaking of being elegant--Kirill [proposed][1] indeed proposed better solution. [1]: http://stackoverflow.com/questions/1339470/how-to-get-the-address-of-the-stdvector-buffer-start-most-elegantly/1339531#1339531
Pavel Shved
+6  A: 

Well, you can remove one set of parens:

&buffer[0]

but that is the common, idiomatic way of doing it. If it really offends you, I suppose you could use a template - something like:

template <typename T> 
T * StartOf( std::vector <T> & v ) {
    return &v[0];
}
anon
Once you do this in a function, you might as well use it to check whether the container is empty.
sbi
I disagree. In the kind of code that I write that could use this function (I don't use it in fact) the check would not be needed
anon
It might be different for you, but where I worked in the last decade or so, I'd put code like this into everybody's toolbox. And since I have very little control over what everybody else is doing, I'd put at least a debug check into this no matter what my own needs would be.
sbi
Well, the philosophy of C++ has always been "don't pay for what you don't use" and it's a philosophy I strongly agree with. Of course, you have no control of what people do with your APIs, but that's their problem, not yours.
anon
sbi
Seems a perfect use for an "assert" to me.
John Burton
+3  A: 

Elegant way would be to change callFunction or to write wrapper for it as follows:

void callFunction( std::vector<TCHAR>::iterator begin_it, std::vector<TCHAR>::iterator end_it )
{
  callFunction( &*begin_it, std::distance( begin_it, end_it ) );
}

int neededLength = computeLength();
std::vector<TCHAR> buffer( neededLength );
callFunction( buffer.begin(), buffer.end() );

You could even make wrapper for all such functions (with different types, not only TCHAR):

template<typename T>
void callFunction( T begin_it, typename std::vector<typename T::value_type>::iterator end_it )
{
  callFunction( &*begin_it, std::distance( begin_it, end_it ) );
}

Type T will be properly deduced and you'll be able still write callFunction( buffer.begin(), buffer.end() );

Kirill V. Lyadvinsky
But this only covers the case when the container/sequence is the only thing to be passed to the function. Sure, you can extend this to add arbitrary additional arguments, but the resulting template mess is not pretty at all -- and prettiness was a goal. (And the version taking iterators should -- at least in a comment -- specify that it needs forward iterators at least.)
sbi
It takes *only* iterators of std::vector. They are forward iterators. No need for additional comments.
Kirill V. Lyadvinsky
@Kirill: Regarding the iterators you are right. That was a brainfart of mine. Sorry.
sbi
+1  A: 

As already said, no.

The reason is that &buffer[0] is the only way guarantied by the standard to get the adresse of the vector buffer.

Klaim
+2  A: 

The reason it looks ugly is because you're at the borderline of nice and clean C++ style code and nice and clean C style code. The C++ code uses iterators, the C code uses pointers and sizes.

You could create some glue to circumvent these problems:

template< typename at_Container, typename at_Function >
void for_container( at_Container& c, at_Function f ) {
    f( &c[0], c.size() );
}

and call it in the client code.

void afunction( int* p, size_t n ) { 
   for( int* p = ap; p != ap+n; ++p ) {
     printf( "%d ", *p );
   }
}

void clientcode() {
   std::vector<int> ints(30,3);
   for_container( ints, afunction );
}
xtofl
+2  A: 

For function like these, I use a utility class, SizedePtr<T> that basically holds a pointer and an element count. A set of converter functions create the SizedPtr<T> from different inputs. So the call changed to:

vector<TCHAR> foo;
callFunction(sizedptr(foo));

One could even add an implicit std::vector constructor to SizedPtr, but I wanted to avoid this dependency.

This helps only if callFunction is under your control. It is a pleasure to work with if you work with different vector types in one application and you want to consolidate. If you generally work with std::vector, it's mostly pointless.

Rougly:

template<typename T>
class SizedPtr
{
    T * m_ptr;
    size_t m_size;
  public:
    SizedPtr(T* p, size_t size) : ... {}
    T * ptr() { return m_ptr; }
    size_t size() const { return m_size; }

   // index access, STL container interface, Sub-Sequence, ...

}

The idea behind is to separate the operation - manipulating a contiguous sequence of elements - from the storage (std::vector). It's similar to what STL does with iterators, but avoids template infection.

peterchen
+5  A: 

Actually, the main problem with &buffer[0] (note the absence of parantheses) isn't that it isn't really pretty. (That's subjective anyway. I remember finding buffer.begin(), buffer.end() not pretty at all, when I first learned to use the STL.)

The main problem is that it invokes undefined behavior whenever buffer is empty -- and most code never checks for that. That's why I put these into my toolbox:

template <class T, class TAl>
inline T* begin_ptr(std::vector<T,TAl>& v)
{return  v.empty() ? NULL : &v[0];}

template <class T, class TAl>
inline const T* begin_ptr(const std::vector<T,TAl>& v)
{return  v.empty() ? NULL : &v[0];}

template <class T, class TAl>
inline T* end_ptr(std::vector<T,TAl>& v)
{return v.empty() ? NULL : (begin_ptr(v) + v.size());} 

template <class T, class TAl>
inline const T* end_ptr(const std::vector<T,TAl>& v)
{return v.empty() ? NULL : (begin_ptr(v) + v.size());}

Using these, you can write your code as

callFunction( begin_ptr(buffer), buffer.size() );

Whether begin_ptr(buffer) is prettier than &buffer[0] is left for you to decide. However, given that NULL should be checked for every pointer function argument, it definitely is more safe.

sbi
+1 for all, except the `NULL` C-ism.
@mmutz: I'm not strongly pro or contra `NULL`/`0`. It's mostly a question of style (although `NULL` is better to grep for). In fact, in the original code I used `0`. I only changed this when I copied the code into the posting, since I thought I see more `NULL` here than `0`. :o>
sbi
http://www.research.att.com/~bs/bs_faq2.html#null
@mmutz: I know about `nullptr`. I suppose I'll switch to that ASAP.
sbi
+1  A: 

If you're using std::vector just for its RAII properties (so that it will free the memory for you), and you don't actually need it to resize or anything, you might be better off using a Boost scoped_array

boost::scoped_array<TCHAR> buffer( new TCHAR[neededLength] );
callFunction( buffer.get(), neededLength );

scoped_array will call delete[] on the array when it goes out of scope.

cibyr