views:

327

answers:

3

Hi! Well, I'm new to operator overloading, and I found this problem. Instead of documenting myself, I prefer to ask you :D

The point is, I know how to do simple operator overloading, but I'm facing problems with stacking operators. I'll try to put a relatively simple example:

struct dxfdat
{
 int a;
 string b;

 /* here is the question */
}

/* use: */
dxfdat example;

example << "lalala" << 483 << "puff" << 1029 << endl;

"lalala" << 483 << "puff" << 1029 << endl shall be stored in b.

dxfdat& operator<< (T a) and things like that work with one parameter (example << 7), but I would like it to work in a 'cout' fashion.

Sorry to be so lazy.

EDIT:

The real thing... Ok, it is a little bit trickier... actually, b isn't a string, but a vector of other objects, and example << "lalala" << 483 << "puff" << 1029 << endl should just create just one object.

This is what I'm trying (translated), though I have no clue on how to tell it when to create the object (as it goes from left to right, doesn't it?):

struct dxfDato
{
    dxfDato(int c = 0, string v = 0, int t = 0) { cod = c; val= v; ty = t; }

    int ty;
    int cod;
    string val;
};

struct dxfItem
{
    int cl;
    string val;
    vector<dxfDato> dats;
    vector<dxfItem> sons;

    template <class T>
    dxfItem &operator<<(const T &t)
    {
        dxfDato dd;
        std::stringstream ss;
        ss << t;
        val = ss;
        dats.push_back(dd); // this way, it creates a lot of objects
        return d;
    }

};

dxfItem headers;

headers << "lalala" << 54789 << "sdfa" << 483 << endl;
// this should create *just one object* in dats vector,
// and put everything on string val

Thanks for everything,

Note: I had to extract and translate a lot of things to put it here, so I didn't check the code for stupid errors.

(Sorry for expanding the question that much, please tell me if I'm misusing stackoverflow's question system)

+6  A: 

The trick is to return a reference to youself from operator << - this way, the operator can be 'stacked'.

class me {

    me& operator<<(int t) {...; return *this;}
};

me m;
m << 4 << 5 << 6;

Just overload the shift operator for all types you wish to support (or make it a template if you can afford the danger)!

Alexander Gessler
Any way you can know when is the last operation? Because -actually- the object is not a string, but a vector where I have to create a object with the info (see EDIT). Thx for your reply.
huff
I'd just add a compulsory end element (similar to `std::endl`).
Alexander Gessler
That's a good idea. I'll try to go in that direction.I wonder if there's another way, though.
huff
You could probably do something clever with a proxy object, but I doubt its worth the obfuscation and hassle.
Andy
+2  A: 
template <typename T>
dxfdata & operator <<( dxfdata & d, const T & t ) {
   std::ostringstream os;
   os << t;
   d.b += os.str();
   return d;
}
anon
Thanks for your reply. Actually, I was trying something similar -meaning, with the return and the two parameters. Though my code was badly expressed and not operating... because of the problem to solve being a little bit more complex -as you can see in EDIT... Thx again!
huff
+1  A: 

It's quite easy, don't panic :)

You have recognized the problem well: it's very similar to the std::cout - std::endl work.

You could do like such, though I'll rename the types, if you don't mind.

struct EndMarker {};
extern const EndMarker end; // To be defined in a .cpp

class Data
{
public:
  Data(): m_data(1, "") {}

  // Usual operator
  template <class T>
  Data& operator<<(const T& input)
  {
    std::ostringstream aStream;
    aStream << input;
    m_data.back() += aStream.str();
  };

  // End of object
  Data& operator<<(EndMarker) { m_data.push_back(""); }

private:
  std::vector<std::string> m_data;
}; // class Data

It works by adding to the current last element by default, and pushing an empty element at the end.

Let's see an example:

Data data;
data << 1 << "bla" << 2 << end << 3 << "foo" << end;

// data.m_data now is
// ["1bla2", "3foo", ""]

The other solution would be to keep a flag (boolean) to store if a end has been streamed or not, and if it has, creating a new element on the next insertion (and erasing the flag).

It a bit more work on insertion, but you don't have the empty element... your call.

Matthieu M.