8 Replies - 1354 Views - Last Post: 16 March 2011 - 01:21 PM Rate Topic: -----

#1 ostreamoverlord  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 3
  • Joined: 12-March 11

how do I store the contents of an ostream before it gets passed to cou

Posted 12 March 2011 - 12:24 PM

Hey guys, in a program I'm working on, I have overloaded the << operator of a class so that I can manipulate the incoming ostream data passed to the class, example:

std::cout << MyClass(arg1, arg2, arg3) << "Hello good sir, please modify this string.";


and in the class called MyClass, I have a function:

ostream &operator<<( ostream &ostaddr, MyClass myptr ) {

	ostaddr << "Hey, I am writing to the front of the stream! ";

        return ostaddr;
}



and this will return to cout : Hey, I am writing to the front of the stream! Hello good sir, please modify this string.

But in the operator<< function I would like a way to be able to store the incoming stream, do some other stuff, and pass the incoming string back to cout untouched, like the following (just an example, doesn't work):

ostream &operator<<( ostream &ostaddr, MyClass myptr ) {


	stringsteam mystream = ostaddr;

	DBClass mydb;

	mydb.passmeastring(mystream.str());


	ostaddr << "Hey, I am writing to the front of the stream! ";

        return ostaddr;
}



Can anyone help me with this?? Thanks

Is This A Good Question/Topic? 0
  • +

Replies To: how do I store the contents of an ostream before it gets passed to cou

#2 #define  Icon User is offline

  • Duke of Err
  • member icon

Reputation: 1276
  • View blog
  • Posts: 4,401
  • Joined: 19-February 09

Re: how do I store the contents of an ostream before it gets passed to cou

Posted 12 March 2011 - 01:23 PM

Usually, data is extracted from an input stream, this data can be stored in the class. The stored data can then be inserted in an output stream.

The streams are not manipulated themselves, data is extracted or inserted.


Class level ostream operator and istream operator

This post has been edited by #define: 12 March 2011 - 01:25 PM

Was This Post Helpful? 0
  • +
  • -

#3 Bench  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 854
  • View blog
  • Posts: 2,338
  • Joined: 20-August 07

Re: how do I store the contents of an ostream before it gets passed to cou

Posted 12 March 2011 - 02:13 PM

Remember your operator associativity to understand why what you're doing is not possible with the built-in std::ostream type:

std::cout << "hello world" << std::endl;"

is the same as
std::cout << "hello world";
std::cout << std::endl; 
because << is left-right associative, In other words, the leftmost expressions are always evaluated before the expressions to the right.

However, there's plenty of ways to get the functionality you want.
  • Overloading another operator for your class which has higher precedence e.g. operator +
  • Creating your own extended/wrapper class which overloads the << operator
  • use an intermediate object which stores the result of your operation; then create this object first and pass the intermediate object to std::ostream instead
  • Create a simple helper function which builds the string you want using a stringstream


Lastly, remember that operator overloading is mostly just a syntactic nicety. there's nothing you can't do with an overloaded operator which you also can't do with a function or member-function.

This post has been edited by Bench: 12 March 2011 - 02:38 PM

Was This Post Helpful? 0
  • +
  • -

#4 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2247
  • View blog
  • Posts: 9,237
  • Joined: 18-February 07

Re: how do I store the contents of an ostream before it gets passed to cou

Posted 13 March 2011 - 12:18 AM

Well here is a pseudo example of what you were looking for I think. To collect the data you have to either use a static/global variable or create an instance of an object. I choose an instance.

Note that you have to use parentheses to make this work because as Bench pointed out the operator<< is left associative:

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

class Collector {
    stringstream allData;
    stringstream lastData;
    
    string getLastData() {
        string retval = lastData.str();
        lastData.str("");
        return retval;
    }
    
    public:
    
    string getData() { return allData.str(); }
    
    friend ostream& operator<<(ostream& out, Collector& col);
    
    template<typename T>
    friend Collector& operator<<(Collector& col, const T& data);  
};




ostream& operator<<(ostream& out, Collector& col) {
    return (out << col.getLastData()).flush();
}


template<typename T>
Collector& operator<<(Collector& col, const T& data) {
    col.lastData << data;
    col.allData << data;
    return col;
}


int main() {

    Collector collect;
    
    cout << (collect << "This is a test: " << 3.14 << ' ' << 159 << " " << 265);
    
    cout << endl << "data collected was: \"" << collect.getData() << "\"" << endl;

    return 0;

}


Of course we could probably ditch the parentheses if we really wanted to but I think it would cause other problems later.

here is a very beta version without the parentheses but there seems to be an issue with an extra newline getting in there somewhere. Anyway it is very late here and I am too tired to think strait so I will just leave it here:
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

typedef ostream& (*FuncManip)(ostream&);

class Collector {
    stringstream allData;
    stringstream lastData;
    
    ostream* currentOut;
    
    string getLastData() {
        string retval = lastData.str();
        lastData.str("");
        return retval;
    }
    
    public:
    
    string getData() { return allData.str(); }
    
    friend Collector& operator<<(ostream& out, Collector& col);
    
    template<typename T>
    friend Collector& operator<<(Collector& col, const T& data);  
    
    friend Collector& operator<<(Collector& col, FuncManip data);
};






Collector& operator<<(ostream& out, Collector& col) {
    col.currentOut = &out;
    return col;
}


template<typename T>
Collector& operator<<(Collector& col, const T& data) {
    col.lastData << data;
    col.allData << data;
    return col;
}


Collector& operator<<(Collector& col, FuncManip fman) {
    col.allData << fman;
    col.allData << fman;
    (*(col.currentOut) << col.getLastData()).flush();
    return col;
}


int main() {

    Collector collect;
    
    cout << collect << "This is a test: " << 3.14 << ' ' << 159 << " " << 265 << endl;
    
    cout << endl << "data collected was: \"" << collect.getData() << "\"" << endl;

    return 0;

}


*note that I don't think this is the best programming practice for real world applications -- but it is fun to play with.

This post has been edited by NickDMax: 14 March 2011 - 02:44 PM
Reason for edit:: Fixed code tags

Was This Post Helpful? 0
  • +
  • -

#5 ostreamoverlord  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 3
  • Joined: 12-March 11

Re: how do I store the contents of an ostream before it gets passed to cou

Posted 14 March 2011 - 01:57 PM

Thanks for the relies guys. The reason I need this to work without parentheses is because I am using this for logging purposes. There is a define in the MyClass that is like the following:

#define MYLOG( arg ) ( MyClass::set_special() & MyClass::arg ) && ( std::cout << MyClass(arg, arg arg) )

and in hundreds of different sourcefiles I have lines like the following:

MYLOG (error) << "We have a problem on line 29. Debug me" << endl;


MYLOG (general) << "Problem with parsing serial port... debug me" << endl;
Was This Post Helpful? 0
  • +
  • -

#6 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2247
  • View blog
  • Posts: 9,237
  • Joined: 18-February 07

Re: how do I store the contents of an ostream before it gets passed to cou

Posted 14 March 2011 - 03:46 PM

Interesting idea, however the maybe just some better ways to do this...


fist of all have you ever looked at Log4C++ as it may be more what you are looking for.


Secondly you don't need to "redirect" the operator<< function at all. Why not just make a logging class?

here is an example:
#include <iostream>
#include <streambuf>
#include <ostream>
#include <fstream>
#include <string>

using namespace std;


/** <Gleened from the internet> - unknown author **/
template <class cT, class traits = std::char_traits<cT> >
class basic_nullbuf: public std::basic_streambuf<cT, traits> {
    typename traits::int_type overflow(typename traits::int_type c)
    {
        return traits::not_eof(c); // indicate success
    }
};

template <class cT, class traits = std::char_traits<cT> >
class basic_onullstream: public std::basic_ostream<cT, traits> {
    private:
    basic_nullbuf<cT, traits> m_sbuf;

    public:
    basic_onullstream():
    std::basic_ios<cT, traits>(&m_sbuf),
    std::basic_ostream<cT, traits>(&m_sbuf)
    {
        init(&m_sbuf);
    }

};

typedef basic_onullstream<char> onullstream;
typedef basic_onullstream<wchar_t> wonullstream;

onullstream nout;
/** </Gleened from the internet>**/

enum LogLevel {
    LOG_NONE,
    LOG_ERROR,
    LOG_WARNING,
    LOG_INFO,
    LOG_NUM_LEVELS
};

const char * LoggingLevels[] = {
    "",
    "ERROR",
    "WARNING",
    "INFO",
};

        

class MyLogger {
    static ostream *logout;
    public:
    static void setLogStream(ostream& os) { logout = &os; }

    LogLevel thisLevel;

    explicit MyLogger(LogLevel level = LOG_NONE) : thisLevel(level) { }

    ostream& logStream() const { return (thisLevel == LOG_NONE || logout == NULL) ? nout : *logout; }
};

ostream* MyLogger::logout = NULL;

template<typename T>
ostream& operator<<(const MyLogger &mlog, const T& data) {
    return (mlog.logStream() << LoggingLevels[mlog.thisLevel] << data);
}

#define LOG(level) MyLogger(level) << ": " __FILE__  << "(" << __LINE__ << "): "

int main() {
    MyLogger::setLogStream(cout);
    
    LOG(LOG_ERROR) << "I don't know what is going on here" << endl;
    LOG(LOG_NONE) << "This should not print" << endl;
    
    for(int i = LOG_NONE; i < LOG_NUM_LEVELS; i++) {
        LOG(static_cast<LogLevel>(i)) << "Testing different logging levels(" << i << ")" << endl;;
    }
    
    ofstream logfile("TestLog.log");
    if (!logfile) {
        LOG(LOG_ERROR) << "Could not open log file" << endl;
    }
    MyLogger::setLogStream(logfile);
    
    LogLevel logLevel = LOG_INFO;
    
    LOG(logLevel) << "This should be redirected to a file" << endl;

    logfile.close();
    return 0;
}

This post has been edited by NickDMax: 14 March 2011 - 05:23 PM
Reason for edit:: Updated code to work with MinGW/Borland

Was This Post Helpful? 0
  • +
  • -

#7 ostreamoverlord  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 3
  • Joined: 12-March 11

Re: how do I store the contents of an ostream before it gets passed to cou

Posted 16 March 2011 - 08:56 AM

Hi NickDMax, thanks for all the help so far. Do you know of a way I can store the stream (or make a copy of it), so that I can send it as a string to another function? perhaps as a database query?

Since everything has to be done on one line, I am unable to have commands like :
MyLogger::setLogStream(cout);
LogLevel logLevel = LOG_INFO;
logfile.close();

Since it is a macro being inserted into hundreds of source files, I only get one line, like the following:
LOG(logLevel) << "This should be redirected to a file" << endl;

therefore all the "work" has to be done in the MyLogger class.

Idealy I would like something like this:
ostream& operator<<(const MyLogger &mlog, const T& data) {
	string mystring = data;
        mlog.sendtodatabase(mystring);
        return data;
}




is this possible? or should I just change my game plan entirely?

Thanks
Was This Post Helpful? 0
  • +
  • -

#8 Bench  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 854
  • View blog
  • Posts: 2,338
  • Joined: 20-August 07

Re: how do I store the contents of an ostream before it gets passed to cou

Posted 16 March 2011 - 01:07 PM

You could use NickDMax's suggestion for declaring a bunch of global variables (yes, really!) whose lifetime and scope span your entire program; no need to keep opening/closing them for every part of your program.

Typically I wouldn't have thought you'd ever want your log file to actually be closed in the middle of your program - if you use log4cxx, you'd probably want to let it lock the log files open til the program closes (you can use different logging methods/configurations to make sure the log files are never at full capacity, such as Rolling File Appenders).

Note: This is one of the few situations I can think of where I would advocate global variables or singletons as a reasonably good/useful thing - especially since Log4cxx is thread safe IIRC.

Also, you can have macros spanning multiple statements..
#define LOG(stream, level)                                      \
    MyLogger::setLogStream(stream);                             \
    MyLogger(level) << ": " __FILE__  << "(" << __LINE__ << "): " 

This post has been edited by Bench: 16 March 2011 - 01:12 PM

Was This Post Helpful? 1
  • +
  • -

#9 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2247
  • View blog
  • Posts: 9,237
  • Joined: 18-February 07

Re: how do I store the contents of an ostream before it gets passed to cou

Posted 16 March 2011 - 01:21 PM

Hoestly if you are really interesting in writing a robust logging system like Log4Cxx then you are going to want to create your own stream class (extend basic_ostream<>) -- especially if you want to enable things like writing to a database/file/io-port/socket etc.

I would suggest reading over the Standard C++ Library Iostreams and Locales User's Guide from Rogue Wave Software (one of the implementors of the standard library - also where many of the standards committee members work). This should give you a deeper understanding of how iostreams work.

As for your problem at hand the above example I gave can work with any ostream. You can write to a file or a database (if you have some kind of dbostream class) or even send text-messages if you write an smsostream class. You don't have to copy streams since it saves a pointer to the current logging stream -- note however there is only 1 logging stream. You could change this so that there were multiple logging streams and different ones are chosen based upon some criteria.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1