Wednesday, April 6, 2011

Is there a better way to print a string with cout up to N characters?

-edit- I am sending binary and not a string. My test is using html pages so in this example i am only using a string but my question is about binary, vectors and debugging with ostream. I make this clears some confusion.

I have the following code:

cout << string(&v[0]).substr(0, len);

Is there a better way to print the string v with cout up the length len? I thought of doing v[len] = 0 but I an assertion is thrown with a size of 1. My code is:

vector<char> v;
v.reserve(1024*16); //required
v.resize(1); //so we can do &v[0]
recv(sockfd, &v[0], v.capacity(), 0);
while (l > 0)
{
 cout << string(&v[0]).substr(0, l);
 recv(sockfd, &v[0], v.capacity(), 0);
}
cout << "the size is " << v.size();
From stackoverflow
  • v[l] is an off by one error. when you receive l characters, then the buffer has been filled from [0..l-1]
    The code makes me shiver a bit, I'd have just used a simple char[] buffer. Additionally, I don't know are you expecting wchar_t's over the wire?

  • vector<char> v;
    v.reserve(1024*16); //required
    v.resize(1); //so we can do &v[0]
    recv(sockfd, &v[0], v.capacity(), 0);
    

    That code has a bug. Calling reserve will only guarantee you that you can push_back at least that many elements until references and iterators to the vector are invalidated again (through a possible reallocation of the used buffer). It will not allow you to write into v[0..1024*16-1], as you do there with recv. You have to do

    v.resize(1024*16); //required
    

    to actually have that many elements available and actually pass v.size() instead of v.capacity().

    For your substring operation, i would probably do

    std::string str(&v[0], n);
    std::cout << str;
    

    Where n ranges from 0 up to v.size(). You can use std::min(n, v.size()) to guarantee that, if n could be larger in your case and you need an upper limit.

    (on a side node, i would avoid having a variable called "l" (ell) there, because it can look very much like an "1" (one), which can confuse the hell out of people)

    acidzombie24 : from what i read you can in fact write into 0-1024*16-1 elements and it is guaranteed to be continuous, however elements from size to capacity are not constructed/initialized.
    Johannes Schaub - litb : it's not right, because the vector does not have so many elements. you can only write to/ read from up to v[0..v.size()-1] of course.
    Johannes Schaub - litb : btw, if you need a constant sized buffer, it's better to use boost::array v; then pass v.data() and v.size() . or an array on the stack - though raw arrays should be avoided... u know, danger around the corner
    flodin : std::string str(v.begin(), v.begin()+n); would be less hackish.
  • See comments: I stand corrected. I was mininformed. However, I still think it's nuts to rely on internals like this. The last microsoft compiler I used violated C99 standards causing me no end of grief. If they can't get return values right on vsnprinf() or new, do you really want to rely on errata like this?

    You are making assumptions regarding how vector is implemented. You are assuming that v[1] comes right after v[0] in memory.

    There is a difference between char buf[]; & buf[1] == & buf[0] + 1 and vector v; & v[1] == & v[0] + 1. The char array uses pointer arithmetic. The vector uses operator[]. How the vector stores data internally, whether it is adjacent or not, is up to that vector class.

    While your code may still work, this is still a BAD thing! It makes your software brittle, causing it to break in strange and expected ways when you least expect it!

    This is a ideal situation for a temporary char array on the local stack. The size is small. You have a hardcoded maximum size.

    If the size wasn't constant, I'd still use a small local char-array buffer on the stack. I'd just append it to a C++ std::string after each iteration. (Yes, std::strings can store binary values including multiple null characters.)

    recv() returns how many bytes it read. Vector v doesn't automagically pick this up. So you need to store and use that value.

    I suggest:

    #define BUFFER_SIZE  (1024*16)
    #define FLAGS        0
    
    int  received = 0;
    int  total    = 0;
    char buffer [ BUFFER_SIZE + 1 ];
    
    memset( buffer, 0, BUFFER_SIZE + 1 );
    
    received = recv( sockfd, buffer, BUFFER_SIZE, FLAGS );
    
    if ( received > 0 )
    {
      copy( buffer + total,
            buffer + total + received,
            ostream_iterator<char>(cout) );
    
      total += received;
    }
    
    while( (received > 0) && (total < BUFFER_SIZE) )
    {
      received = recv( sockfd, buffer + total, BUFFER_SIZE - total, FLAGS );
    
      if ( received > 0 )
      {
        copy( buffer + total,
              buffer + total + received,
              ostream_iterator<char>(cout) );
    
        total += received;
      }
    }
    
    buffer [ total ] = '\0';
    buffer [ BUFFER_SIZE ] = '\0';
    
    cout << "The total size is " << total << endl;
    
    Johannes Schaub - litb : well. actually vector being contiguous is one of its key features. you are guaranteed that &v[1] == &v[0] + 1 is true if the vector contains at least 2 elements.
    Johannes Schaub - litb : it wasn't that way in c++98. but it was fixed in c++03. read http://herbsutter.wordpress.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/
    Mr.Ree : I stand corrected. Original posting updated. But I still think it's nuts to rely on internals like this. The last microsoft compiler I used violated C99 standards causing me no end of grief. Do you really want to rely on errata like this? Especially when there's no need?
    bk1e : When did Microsoft compilers ever claim to support C99?
  • Why are you setting the size to 1?
    When you reserve the space the space is available for the vector to grow into (without reallocating). But who said it was safe to use directly? I have seen (debug) implementations that add a warning buffer just after size() modify these bits and it would generate an assert next time it checked. You should only be reading/writing from 0 -> size().

    NB This will then also allow you to use v[len] = '\0';

    vector<char> v(1024*16);
    
    std::size_t  len = recv(sockfd, &v[0], v.size(), 0);
    while (len > 0)
    {
        v[len] = '\0';
        cout << &v[0];
        len = recv(sockfd, &v[0], v.size(), 0);
    }
    

    Note this is probably not the best way to read a string.
    I would pass length information over the stream so you know when there is not more information to read then read only as much as is required.

    acidzombie24 : If i use do v.clear(); v.push_back(data) a bunch of times. I would need to resize it capacity again? which would construct all my chars and in chars case no ctor but initial it to fillData? thats annoying, especially if i write/send many 128bytes and need a buf of at least 16k. Is there a way to...
    acidzombie24 : is there a way to resize w/o initializing the data to fillValue? if not that debug impl would annoy me in this prj and it be debug imple vs avoiding potentially a lot of overhead with resize. also, ostream.write(ptr, len); solves my question.
  • You can use the method ostream::write on the cout object:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    int main()
    {
      vector<char> test;
      test.push_back('a');
      test.push_back('b');
      test.push_back('c');
    
      cout.write(&test[0], 3);
      cout << endl;
    }
    

    Outputs:

    abc

    Since ostream::write returns an ostream& containing *this, you can even do

    cout.write(&test[0], 3) << endl;
    

    but I'm not sure that is actually any better (or clearer).

0 comments:

Post a Comment