16

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?

Martin B
  • 23,670
  • 6
  • 53
  • 72
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • 18
    You should rename buffer to elegantBuffer... :) –  Aug 27 '09 at 07:55
  • Can't you get callFunction to accept a vector? this seems to me to be the actual elegant way. Otherwise you're stuck in the unelegant no-mans land between C and C++. – jilles de wit Aug 27 '09 at 08:30
  • @bismuth: I know it would be a nice solution to change the consumer function to accept the vector, but it is not an option. In fact I only use the vector as an allocate-once-array of size unknown in advance. – sharptooth Aug 27 '09 at 08:56
  • If all you're using it for is allocate-once you could sacrifice the convenience of scope-automatic deletion and use TCHAR* buffer = new TCHAR[neededLength]; Just remember to delete [] buffer when it's done. – cheshirekow Mar 11 '11 at 23:30
  • 5
    @cheshirekow: *Just remember* is the key problem here. If an exception is thrown `delete[]` will not run, so I have to use a separate try-catch just for `delete`ing the buffer. That's why `vector` or something similar is preferable. – sharptooth Mar 14 '11 at 06:19

11 Answers11

37

It's really odd that nobody know this!!! in C++11 you could use:

buffer.data()

it could get the address of the vector I have test it:

vector<char>buffer;
buffer.push_back('w');
buffer.push_back('h');
buffer.push_back('a');
buffer.push_back('t');
buffer.push_back('\0');
char buf2[10];
memcpy(buf2,buffer.data(),10);

Specification here.

wcochran
  • 10,089
  • 6
  • 61
  • 69
hyphen
  • 369
  • 3
  • 4
21

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];
}
wcochran
  • 10,089
  • 6
  • 61
  • 69
  • 7
    Once you do this in a function, you might as well use it to check whether the container is empty. – sbi Aug 27 '09 at 11:42
  • 1
    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 –  Aug 27 '09 at 14:05
  • 3
    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 Aug 30 '09 at 20:48
  • 2
    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. –  Aug 30 '09 at 20:57
  • @Neil: In the end it's a question of defining the template's spec: are users allowed to call it with empty vectors or not? In the projects I worked in, disallowing it would render the thing useless -- if users remember to check for emptry containers before handing out pointers into them, you can expect them to know about the `&v[0]` idiom... – sbi Aug 31 '09 at 23:02
  • 8
    Seems a perfect use for an "assert" to me. – jcoder Aug 08 '10 at 08:32
19

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
  • 219,715
  • 46
  • 258
  • 445
  • 2
    @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 Aug 27 '09 at 13:32
  • @mmutz: I know about `nullptr`. I suppose I'll switch to that ASAP. – sbi Aug 28 '09 at 13:24
12

but this &(buffer[0]) looks ugly

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

&buffer[0]
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
6

No.

P Shved
  • 96,026
  • 17
  • 121
  • 165
  • 2
    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 Aug 27 '09 at 07:58
  • "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 Aug 27 '09 at 08:31
  • 1
    By renaming buffer to just b you get an even lower character count! – graham.reeds Aug 27 '09 at 08:32
  • 1
    @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 Aug 27 '09 at 11:20
  • I think that mixing prefix and postfix operations where postfix one (operator []) _preceedes_ the prefix one (operator &) just messes things up. So I just didn't think about parenthesis here; what makes me deserving less rep--I must consider that others may have different tastes. – P Shved Aug 27 '09 at 17:09
  • 1
    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 – P Shved Aug 27 '09 at 17:12
4

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

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
3

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

// legacy function
void callFunction( TCHAR* buf, int buf_size)
{
  // some code
}

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

// somewhere in the code
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 (as std::vector<sometype>) and you'll be able still write callFunction( buffer.begin(), buffer.end() );.

Note that you cannot declare template function as void callFunction( typename std::vector<typename T::value_type>::iterator begin_it, typename std::vector<typename T::value_type>::iterator end_it ) as someone proposed recently as an edit to this answer, because in that case you will get the deducion error.

Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • 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 Aug 27 '09 at 11:41
  • It takes *only* iterators of std::vector. They are forward iterators. No need for additional comments. – Kirill V. Lyadvinsky Aug 27 '09 at 12:22
  • @Kirill: Regarding the iterators you are right. That was a brainfart of mine. Sorry. – sbi Aug 27 '09 at 13:34
2

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
  • 40,723
  • 12
  • 105
  • 192
2

For functions like these, I use a utility class, SizedPtr<T> that basically holds a pointer and an element count. A set of converter functions creates the SizedPtr<T> from different inputs. So the call changes 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.

Roughly:

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 this 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.

wrapperapps
  • 937
  • 2
  • 18
  • 30
peterchen
  • 40,917
  • 20
  • 104
  • 186
1

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
  • 67,274
  • 36
  • 133
  • 188
1

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
  • 743
  • 1
  • 7
  • 12