3

Say I have a vector of chars and I pushed it into a stream as a string, rather than a vector of chars, how would i get back the vector of chars using operator>>?

class C{
    private:
        vector<char> c;

    public:
        C(string str){
          for(int x = 0; x < str.size(); x++)
              c.push_back(str[x]);
        }

        vector<char> data(){
           return c;
        }       
};

ostream operator<<(ostream & stream, C & in){
   for(int x = 0; x < in.data().size(); x++)
      stream << in.data()[x];
   return stream;
}

istream operator>>(istream & stream, C & in){
    // ???
    // what kind of loop?
}
zyzzva
  • 31
  • 1
  • 1
  • 2
  • As a sidenote, don't pass possibly large objects by value. Use const references in `data()` and `<<`. And of course you have to use a reference in `>>`. – Christian Rau Jun 26 '11 at 22:52
  • 1
    And prefer a const reference when outputting, as you may be called with an rvalue. – Puppy Jun 26 '11 at 23:00
  • 1
    If you're new here, don't forget to inform yourself about the accept and up-vote features. – Christian Rau Jun 26 '11 at 23:01

5 Answers5

4

I'd write your example like this....

#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>
#include <sstream>

class C
{
    std::vector<char> mData;

  public:
    // Constructor, note im using the iterator range 
    // vector constructor.
    C(const std::string& str)
    : mData(str.begin(), str.end())
    {
    }

    // Returning data by const reference to avoid 
    // copying a potentially large object.
    const std::vector<char>& data() const
    {
        return mData;
    }

    // declared the input operator as a friend as I need it to
    // access mData - alternatively you could write a "loadFromStream"
    // type function that does the same, declare this as non-friend, and call that.
    friend std::istream& operator>>(std::istream& is, C& c);
};

std::ostream& operator<<(std::ostream& os, const C& c)
{
    // Use an ostream_iterator to handle output of the vector
    // using iterators.
    std::copy(c.data().begin(), 
              c.data().end(), 
              std::ostream_iterator<char>(os, ""));

    return os;
}

std::istream& operator>>(std::istream& is, C& c)
{
    // load the data using the assign function, which
    // clears any data already in the vector, and copies 
    // in the data from the specified iterator range.
    // Here I use istream_iterators, which will read to the end
    // of the stream.  If you dont want to do this, then you could 
    // read what you want into a std::string first and assign that.
    c.mData.assign(std::istream_iterator<char>(is),
                   std::istream_iterator<char>());

    return is;
}

int main()
{
    C c("Hello");

    std::stringstream ss;
    ss << c;

    std::cout << ss.str() << std::endl;

    C d("");
    ss >> d;

    std::cout << d.data().size() << std::endl;

    return 0;
}
Node
  • 3,443
  • 16
  • 18
3

You can always construct one from the other:

std::vector<char> A = getVector();
std::string       B = getString();

std::vector<char> newB(B.begin(), B.end());
std::string       newA(A.begin(), A.end());

Using that you should be able to write your in-stream operator, e.g.

std::string stmp;
stream >> stmp;
std::vector<char> vtmp(stmp.begin(), stmp.end());
c.swap(vtmp);
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

To do what >> for std::string does, well, you really just need to use std::string. Note that you need to pass C by reference, not by value, and operators should return the original stream by reference. Also, I can't help but think that using std::vector<char> instead of std::string is not really that useful (plus, the ctor is inefficient — at least reserve str.length() if you're doing this).

istream& operator>>(istream& s, C& in) {
    std::string tmp;
    s >> tmp;
    // you also need to return reference in in.data() for this to work
    // or declare operator>> as friend and use in.c directly
    in.data().assign(tmp.begin(), tmp.end());
    return s;
}
Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
1

First of all, you'll want to return your streams as references instead of by value. Also, data() should return a const reference to the vector, so that it isn't copied (important if it's a large vector).

As for overloading >>, I would try something like:

istream& operator>>(istream& stream, C& in) {
    for (char c; /* some end condition */;) {
        stream >> c;
        in.c.push_back(c);
    }
}

Of course, this approach would require declaring operator>>(istream&, C&) a friend function. The alternative is to supply the equivalent of an append(char) function to your public interface. Also, data() should be marked const, so the whole signature would be const vector<char>& data() const making it clear that it is strictly an accessor.

fulmicoton
  • 15,502
  • 9
  • 54
  • 74
jpm
  • 3,165
  • 17
  • 24
  • It really depends oh how you define your object. If you're looking to convert an entire istream into a C object, then you'll loop until the istream's eof flag is set. Otherwise, you'll wait until you read in some sort of delimiter, which could either be standard for the class, or user defined. – jpm Jun 26 '11 at 23:44
  • For your end condition, you can use eof()/http://www.cplusplus.com/reference/iostream/ios/eof/ – fulmicoton Jun 27 '11 at 08:51
  • You could use stream.eof() as end condition. – fulmicoton Jun 27 '11 at 08:52
1

You can use the istream iterator. Its default constructor initializes to end of stream.

http://www.cplusplus.com/reference/std/iterator/istream_iterator/

Your code would then look something like.

typedef std::istream_iterator<char> charstream_it;
c = std::vector<char>(charstream_it(stream),  charstream_it());

In your constructor, you should probably use the STL iterator style copy constructor as advised by Kerrek SB.

C::C(string str):
c(str.begin(), str.end()) {};
fulmicoton
  • 15,502
  • 9
  • 54
  • 74