Thread: overloading <<

  1. #1
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300

    overloading <<

    I wanna do this:
    Code:
    log << "hey" << 23 << endl;
    I can do this:
    Code:
    log << "hey";
    with this
    Code:
    void logger::operator<< (std::string data) {
    	log << data;
    }
    But anything after the initial string "hey", and 23 or endl, will not fly.

    log is an ofstream. What should "data" be to make this work? I've tried ostream (and istream, actually), neither works. I've also tried overloading the function -- eg, void operator<<(int data) -- which still does not permit a chain like in the first example.

    All the examples on the web for something like this involve:
    Code:
    cin >> log;
    which is not what I want to do.

    Is this even possible of am I asking for too much?
    Last edited by MK27; 03-20-2010 at 05:17 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    The general form of the "stream operators":

    Code:
    std::ostream & operator <<
    (
    	std::ostream & lhs,
    	const type & rhs
    );
    Code:
    std::istream & operator >>
    (
    	std::istream & lhs,
    	type & rhs
    );
    If you want to support similar behavior with your own classes you need to use similar syntax.

    The key to "operator chaining" is simply returning the relevant type. You can't return `void'. This should really be obvious.

    The "built in" types may, trivially, be methods of the class, but if you want to support "expansion" you are going about it the wrong way. There are two good ways. The first is to inherit from `std::basic_ostream<???, ???>' or `std::basic_istream<???, ???>' implementing your class as true children of these classes. The second is to use template functions to forward any type used with the "stream operators" as part of your class to the underlying stream--effectively exporting the stream as a part of your class interface. With either method I only need to write a single "insertion" or "extraction" function for any class I may write. With your approach I'd need to write two such functions.

    Which method you should choose depends on your goal in implementing the class and is largely philosophical. Do you want a "stream" type different from the existing "streams", but still "constrainable as" the existing "stream" types, that provides additional abilities? Or. Do you want a type that simply exports an existing "stream" type as part of the interface?

    Is this even possible of am I asking for too much?
    This is C++; very few things are impossible.

    Soma

  3. #3
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    I'd probably do something like this:
    Code:
    template<typename T>
    logger &logger::operator<<(const T &value)
    {
      log << value;
      return *this;
    }
    That should work. A few improvements:
    - Supports any type supported by iostream.
    - Const reference pass avoids copying.
    - As phantomtap said, returns a reference rather than void.

    Returning void doesn't work as:
    Code:
    a << b << c
    is equal to:
    Code:
    (a << b) << c
    and if "a << b" is of type void, then "(a << b) << c" makes no sense.

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    In the end it seemed easiest to just make the fstream public and not bother with this, but I am still curious as to whether it can be made to work (I now think it cannot).

    Quote Originally Posted by phantomotap View Post
    The general form of the "stream operators":
    If you want to support similar behavior with your own classes you need to use similar syntax.

    The key to "operator chaining" is simply returning the relevant type. You can't return `void'. This should really be obvious.
    The model you are talking about presumes there is a rhs stream, but here the rhs is the object itself:

    Code:
    log << "some data" << 666 << end;
    Here's the idea (with a string as the bucket instead of a file), using something closer to what EVOx suggests:
    Code:
    #include <iostream>
    #include <string>
    using namespace std;
    
    class test {
    	string data;
    	public: 
    		test(){ }
    		template<typename T> test& operator<< (T &val) {
    			data += val;
    			return *this;
    		}
    		string &report() { return data; }
    		~test() {};
    };
    
    int main() {
    	test one;
    
    	one << "hello " << 666 << " -- " << 0.4f;
    
    	cout << one.report();
    
    	return 0;
    }
    The current error is:

    test.cpp: In function ‘int main()’:
    test.cpp:21: error: no match for ‘operator<<’ in ‘one.test:perator<< [with T = const char [7]](((const char (&)[7])"hello ")) << 666’
    test.cpp:10: note: candidates are: test& test:perator<<(T&) [with T = int]
    test.cpp: In member function ‘test& test:perator<<(T&) [with T = const char [7]]’:
    test.cpp:21: instantiated from here
    test.cpp:11: error: no match for ‘operator<<’ in ‘((test*)this)->test::data << val’

    I've played around with this a bit, nothing works -- I suppose I might as well overload +. Either way, you cannot chain/stream the arguments.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by MK27 View Post
    The model you are talking about presumes there is a rhs stream, but here the rhs is the object itself:
    Are you smoking something? The model under discussion is the model that the standard library uses (look it up), which works because the associativity goes from left to right. We don't at all assume there's a rhs stream. But if the return type of the first << is the stream, that becomes the left piece of the next <<, and then that result becomes the left piece of the next <<, and so on.

    As to your example here, it is true that "hello " + 666 is meaningless. (It's not the << that's failing, you see, it's the data += val inside the call that's stupid.)

  6. #6
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    Two bugs in this code:
    Code:
    		template<typename T> test& operator<< (T &val) {
    val isn't a const reference. It should really be one.

    Code:
    			data += val;
    operator+ for a string doesn't support all those parameters like integers and std::endl. So for a standard string it won't work, for a stream it would. You could use an ostringstream and get the string and append that to data.


    Edit: I just tested it... This works:
    Code:
    #include <iostream>
    #include <string>
    #include <sstream>
    using namespace std;
    
    class test {
    	string data;
    	public: 
    		test(){ }
    		template<typename T> test& operator<< (const T &val) {
    			ostringstream oss;
    			oss << val;
    			data += oss.str();
    			return *this;
    		}
    		string &report() { return data; }
    		~test() {};
    };
    
    int main() {
    	test one;
    
    	one << "hello " << 666 << " -- " << 0.4f;
    
    	cout << one.report();
    
    	return 0;
    }
    Last edited by EVOEx; 03-20-2010 at 10:14 AM.

  7. #7
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by tabstop View Post
    Are you smoking something? The model under discussion is the model that the standard library uses (look it up), which works because the associativity goes from left to right.
    Okay, but if I even try using that prototype here (with two params, the rhs stream and the lhs data), I get:

    test.cpp:10: error: ‘test& test:perator<<(std:stream&, T&)’ must take exactly one argument

    There are plenty of examples on the web using that model, but they all revolve around stuff like this:
    Code:
    cin >> obj;
    cout << obj;
    I suppose it is more like an input stream than an output one, so I'd be fine with:
    Code:
    "hello" >> 66 >> obj;
    but the world does not work that way.

    Just ignore the += for now, I see the problem but it is not a compilation error, so it's sort of irrelevant until the code compiles.

    So I'm still of the opinion that you cannot do this:
    Code:
    obj << data << data << data;
    Unless obj is a steam object; you can't overload a stream operator and make it work, without an actual stream (I guess that shouldn't be surprising).
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  8. #8
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    Quote Originally Posted by MK27 View Post
    So I'm still of the opinion that you cannot do this:
    Code:
    obj << data << data << data;
    Unless obj is a steam object; you can't overload a stream operator and make it work, without an actual stream (I guess that shouldn't be surprising).
    Read my previous post. It's kind of a full proof that it is very well possible, isn't it?
    QED.

    As an addition, the STL is implemented using... guess...? C++! Meaning that everything the STL can do, you can do in C++.

  9. #9
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by MK27 View Post
    Okay, but if I even try using that prototype here (with two params, the rhs stream and the lhs data), I get:

    test.cpp:10: error: ‘test& test:perator<<(std:stream&, T&)’ must take exactly one argument

    There are plenty of examples on the web using that model, but they all revolve around stuff like this:
    Code:
    cin >> obj;
    cout << obj;
    I suppose it is more like an input stream than an output one, so I'd be fine with:
    Code:
    "hello" >> 66 >> obj;
    but the world does not work that way.

    Just ignore the += for now, I see the problem but it is not a compilation error, so it's sort of irrelevant until the code compiles.

    So I'm still of the opinion that you cannot do this:
    Code:
    obj << data << data << data;
    Unless obj is a steam object; you can't overload a stream operator and make it work, without an actual stream (I guess that shouldn't be surprising).
    Maybe this is a cheating (I needed a way to turn your arbitrary data into a string object to add it in), but this works too:
    Code:
    #include <iostream>
    #include <string>
    #include <sstream>
    using namespace std;
    
    class test {
        string data;
        public:
        test(){ }
        template<typename T> test& operator<< (const T &val) {
            std::stringstream temp;
            temp << val;
            data += temp.str();
            return *this;
        }
        string &report() { return data; }
        ~test() {};
    };
    
    int main() {
        test one;
    
        one << "hello " << 666 << " -- " << 0.4f;
    
        cout << one.report();
    
        return 0;
    }

  10. #10
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Ah, here's my missing link:
    Code:
    			ostringstream oss;
    			oss << val;
    			data += oss.str();
    Thanks EVOEx. Looks like tabstop came up with something similar.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  11. #11
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    Quote Originally Posted by MK27 View Post
    Ah, here's my missing link:
    Code:
    			ostringstream oss;
    			oss << val;
    			data += oss.str();
    Thanks EVOEx. Looks like tabstop came up with something similar.
    You're welcome. But you should learn something from this. Never -ever- assume something is impossible just because you can't make it so or you can't understand how it'd work. Many people do assume that, but a lot of people have been proven wrong because of that before.

  12. #12
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by EVOEx View Post
    You're welcome. But you should learn something from this. Never -ever- assume something is impossible just because you can't make it so or you can't understand how it'd work. Many people do assume that, but a lot of people have been proven wrong because of that before.
    I agree, but there are some things that are impossible (usually because they are inappropriate and there is a better way to do it).

    In this case I just dinna have enough knowledge of some basic things (like the use of the stringstream). Sometimes you have to settle for where you're at.

    Unless some nice person is willing to bring teach you something
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  13. #13
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    MK27, do you try to understand anything anyone says? Or, as it seems, do you really just glance over the text and make assumptions about the intent?

    I told you what to do in the very first post. EVOEx came by and posted source doing exactly what I told you to do using the exact names and interface you asked for. WHY DIDN'T YOU TRY IT?! Are you broken in some fundamental way?

    Here is the code, EVOEx code, in the appropriate context. It works exactly as you said you wanted in the context you specified. The question is, why do you keep doing this? This is the third of fourth time you've been handed a solution in the first post only to disregard it in favor of the conclusions you've already drawn before you posted.

    I agree, but there are some things that are impossible (usually because they are inappropriate and there is a better way to do it).
    You have so little experience with C++ that your opinion of impossibilities is certainly wrong. If you can't make something work, then ask for help, but if you ask take the time to understand what help is offered.

    In this case I just dinna have enough knowledge of some basic things (like the use of the stringstream).
    YOU DIDN'T HAVE THE KNOWLEDGE OF CHAINING OPERATORS IN C++. THAT KNOWLEDGE WAS GIVEN TO YOU IN THE FIRST TWO POSTS. YOU IGNORED IT IN FAVOR OF CONTINUING A BLIND JOURNEY.

    Your knowledge, or lack thereof, of `std::stringstream' had nothing to do with this.

    Soma

    Code:
    #include <fstream>
    
    struct logger
    {
    	logger():
    		log("somefile.txt")
    	{
    	}
    	template<typename T>
    	logger &operator<<(const T &value)
    	{
    	  log << value;
    	  return *this;
    	}
    	std::ofstream log;
    };
    
    int main()
    {
    	logger mylog;
    	mylog << "Hello, World" << 'C' << 4 << 0.3434234;
    	return(0);
    }

  14. #14
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by phantomotap View Post
    I told you what to do in the very first post.
    No you didn't. You were explaining how to do something else. Go back and read post #4. Since neither I, nor you, nor tabstop, nor EVOEx came up with a solution that works without using a stringstream internally , the stringstream (which I hadn't used before) seems to be essential to what I wanted to do.

    There is no mention or use of this "missing link" until EVOEx did it in the edit to post #6.
    The previous solutions DO NOT WORK and there is sufficient explanation of why already.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  15. #15
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by MK27 View Post
    No you didn't. You were explaining how to do something else. Go back and read post #4. Since neither I, nor you, nor tabstop, nor EVOEx came up with a solution that works without using a stringstream internally , the stringstream (which I hadn't used before) seems to be essential to what I wanted to do.

    There is no mention or use of this "missing link" until EVOEx did it in the edit to post #6.
    The previous solutions DO NOT WORK and there is sufficient explanation of why already.
    If you don't like stringstream use Boost's lexical_cast instead. The part you needed was "conversion to string" which does not ipso facto require a stream object.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Overloading << and side effects
    By Memloop in forum C++ Programming
    Replies: 2
    Last Post: 09-21-2009, 01:30 AM
  2. overloading <<
    By msshapira in forum C++ Programming
    Replies: 2
    Last Post: 05-06-2009, 02:11 PM
  3. Overloading fstream's << and >> operators
    By VirtualAce in forum C++ Programming
    Replies: 2
    Last Post: 04-09-2007, 03:17 AM
  4. Overloading the << operator.
    By antex in forum C++ Programming
    Replies: 5
    Last Post: 05-31-2005, 01:37 AM
  5. Replies: 8
    Last Post: 11-27-2004, 03:12 AM