Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » ostream to printf

This thread is locked; no one can reply to it. rss feed Print
ostream to printf
kazzmir
Member #1,786
December 2001
avatar

I debug my program by putting in statements like debug(0) << "Hello" or debug(1) << "hello" where the number passed in is the debug level. 0 is normal, 1 is slightly more verbose, etc.

I accomplish this with the following code

#SelectExpand
1#include "debug.h" 2#include <iostream> 3 4using namespace std; 5 6static int global_debug_level = 0; 7 8class nullstreambuf_t: public std::streambuf { 9public: 10 nullstreambuf_t():std::streambuf(){ 11 } 12}; 13 14static nullstreambuf_t nullstreambuf; 15 16class nullcout_t: public std::ostream { 17public: 18 nullcout_t():std::ostream(&nullstreambuf){ 19 } 20}; 21 22static nullcout_t nullcout; 23 24ostream & Global::debug(int i, const string & context){ 25 if ( global_debug_level >= i ){ 26 std::cout << "[" << i << ":" << context << "] "; 27 return std::cout; 28 } 29 return nullcout; 30} 31 32void Global::setDebug( int i ){ 33 global_debug_level = i; 34} 35 36int Global::getDebug(){ 37 return global_debug_level; 38}

So either I return cout if the current debug level is less than or equal to the global debug level or I return an ostream object that sends all output to nowhere.

Now I would like another ostream object that uses printf to print. This is because in the dolphin wii emulator it only recognizes output using printf but not cout. Supposedly I could hack the wii internals or some other crazy stuff to get cout to work but I haven't figured it out and I don't really want to.

So can anyone figure out how to make an ostream object use printf?

bamccaig
Member #7,536
July 2006
avatar

You could implement your own std::ostream... Maybe you could do some crazy operator hacks to skip the actual implementation:

class PrintfOstream: public std::ostream {};

PrintfOstream & operator<<(PrintfOstream & stream, const std::string & s)
{
    // Dodge velociraptor attack...
    printf(s.c_str());

    return stream;
}

I'm not sure if that would work or not ...

kazzmir
Member #1,786
December 2001
avatar

Hm ok I tried that before and it didn't work for some reason but I can't remember why now. I'll try this again and see how far I get.

bamccaig
Member #7,536
July 2006
avatar

#SelectExpand
1#include <fstream> 2#include <iostream> 3#include <sstream> 4#include <string> 5 6class PrintfOstream: public std::ostream {}; 7 8PrintfOstream & operator<<(PrintfOstream & stream, const std::string & s) 9{ 10 std::cerr << "Called my special operator... \\o/" << std::endl; 11 12 // Dodge velociraptor attack... 13 printf(s.c_str()); 14 15 return stream; 16} 17 18PrintfOstream & operator<<(PrintfOstream & stream, const char * const s) 19{ 20 return stream << std::string(s); 21} 22 23PrintfOstream & operator<<(PrintfOstream & stream, std::ostream & (*f)(std::ostream &)) 24{ 25 std::stringstream ss; 26 27 ss << f; 28 29 return stream << ss.str(); 30} 31 32int main(int argc, char * argv[]) 33{ 34 PrintfOstream cout; 35 36 cout << "Hurray!" << std::endl; 37}

bash $ g++ -Wall main.cpp
bash $ ./a.out 2>/dev/null
Hurray!
bash $ ./a.out 1>/dev/null
Called my special operator... \o/
Called my special operator... \o/
bash $ 

Evert
Member #794
November 2000
avatar

Quote:

So can anyone figure out how to make an ostream object use printf?

You can use fprintf() to print to a (C) stream. Not sure if that helps.
I would rather make a function (or macro) debug_printf(level, fmt, ...); that basically does if (level>debug_level) printf(fmt, ...);.

bamccaig
Member #7,536
July 2006
avatar

^ That's another very reasonable solution. TBH, the printf family is nicer than iostream except that iostream can be extended to deal with user-defined types in C++. :)

Karadoc ~~
Member #2,749
September 2002
avatar

I'm a great fan of printf; and when I want to use the awesomeness of printf alongside the safety/convenience of c++ features, I use boost::format.

-----------

bamccaig
Member #7,536
July 2006
avatar

boost::format is nice, but it's sort of a compromise. :) With basic C-compatible types printf is still nicer. :) All that said,... the most important WTF is why std::cout and std::cerr (let alone that you're writing debug data to STDOUT :-X) doesn't work on Wii........ :-/

ImLeftFooted
Member #3,935
October 2003
avatar

The simplest way is to using ostringstream and forward the whole thing to printf as a string (ie printf("%s", myStream.str().c_str())).

Figuring out when to execute the print is the hard part IIRC. I'll update my post with an example.

Okay, I thought of the dead simple solution.

#SelectExpand
1#include <sstream> 2 3class Debug { 4public: 5bool ignore; 6template<class T> 7Debug operator <<(const T &t) 8{ 9 if(ignore) return *this; 10 11 ostringstream ss; 12 13 ss << t; 14 15 string s = ss.str(); 16 // Copy string object become some compilers *cough*MSVC*cough* 17 // have faulty temporary object issues that break c_str() w/o this. 18 19 printf("%s", s.c_str()); 20 21 return *this; 22} 23}; 24 25Debug Global::debug(int i, const string & context){ 26 Debug ret; 27 ret.ignore = true; 28 if ( global_debug_level >= i ){ 29 printf("[%d:%s] ", i, context.c_str()); 30 ret.ignore = false; 31 } 32 return ret; 33}

It wont group your cout statements into a single printf -- but your old code didn't do that anyway.

Update: Darn, beaten by bamccaig and his answer was more correct :P.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

bam-boy said:

PrintfOstream & operator<<(PrintfOstream & stream, std::ostream & (*f)(std::ostream &))
{
    std::stringstream ss;

    ss << f;

    return stream << ss.str();
}

What the hell is that? Why would you overload the stream out operator to accept a specialized function pointer that you don't even call? And you don't need to use a stringstream to send a pointer to an ostream. :P::):P

Edit

bammer said:

PrintfOstream & operator<<(PrintfOstream & stream, const std::string & s)
{
    std::cerr << "Called my special operator... \\o/" << std::endl;

    // Dodge velociraptor attack... failed dexterity check... eaten!
printf(s.c_str());
return stream; }

You don't want to directly pass the string to printf, otherwise it could interpret the string as commands. Use %s instead :

printf("%s" , s.c_str());

bamccaig
Member #7,536
July 2006
avatar

@Dustin Dettmer: Your fail in indentation is inexecusable[1] given your experience with the community. I award you no points. May <insert_deity_here> have mercy on your soul. :P

What the hell is that? Why would you overload the stream out operator to accept a specialized function pointer that you don't even call? And you don't need to use a stringstream to send a pointer to an ostream. :P::):P

See std::endl. I'm not sure that I handled it in the most optimized/best way possible, but I do welcome you to correct me (note that I didn't bother checking if e.g., std::hex required more).

You don't want to directly pass the string to printf, otherwise it could interpret the string as commands.

Good catch. :) Except for the terminology. I don't think "commands" is valid in any sense of the word here. Certainly the string that I passed to printf would be interpretted as a format string, but the varargs aren't "commands". Regardless, a better solution would be (assuming the Wii supports it) to use fputs(blah, stdout) (I think).

References

  1. Edit: as is my fail in spelling. :( The good news is that I have invented a word ... for when a stubborn program won't execute?
Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Dustin, you're returning a reference to a stack object that goes out of scope. :(

If you're going to use bamccaig's implementation, you will have to make it (your PrintfOstream object) a member of your debug class or else make it a global. The reason is that you can't call it through an ostream* and expect it to work the same way, because operator << is not virtual, so if you call it from an ostream * or & then you get the ostream implementations of operator <<.

I can't believe I'm saying this, but I actually learned something from bamccaig. ::)
I didn't know manipulators were implemented using functions. I just figured they were separate types with global implementations of operator <<.

ImLeftFooted
Member #3,935
October 2003
avatar

Dustin, you're returning a reference to a stack object that goes out of scope.

Simple mistake, fixed now.

Bam's solution still looks better on the surface.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Bam's solution still looks better on the surface.

Not without some help from your implementation. His only handles strings and manipulators. Yours handles all the other types of data. If you want to be able to affect the stream with manipulators, then you need to have a persistent stream to affect, not a temporary one.

The best of both worlds :

LogStream.hpp

#SelectExpand
1 2#ifndef LogStream_H 3#define LogStream_H 4 5#include <iostream> 6using std::ostream; 7 8#include <sstream> 9using std::stringstream; 10 11#include <string> 12using std::string; 13 14#include <cstdio> 15 16enum DEBUG_LEVEL { 17 NONE = 0, 18 ERROR = 1, 19 WARNING = 2, 20 INFO = 3, 21 VERBOSE = 4 22}; 23 24extern DEBUG_LEVEL global_debug_level; 25 26class pfostream : virtual public std::ostream { 27private : 28 stringstream ss; 29 30public : 31 pfostream() : ss() {} 32 33 template <class Type> 34 pfostream& operator<<(const Type& t); 35 36 pfostream& operator<<(ostream& (*f)(ostream&)); 37}; 38 39template <class Type> 40pfostream& pfostream::operator<<(const Type& t) { 41 ss.str(""); 42 ss << t; 43 string s = ss.str(); 44 printf("%s" , s.c_str()); 45 return *this; 46} 47 48 49pfostream& pfostream::operator<<(ostream& (*f)(ostream&)) { 50 ss.str(""); 51 ss << f; 52 string s = ss.str(); 53 printf("%s" , s.c_str()); 54 return *this; 55} 56 57 58class Logger { 59private : 60 DEBUG_LEVEL debug_level; 61 ostream* out; 62 pfostream* pfout; 63 bool use_pf; 64 65public : 66 Logger() : debug_level(ERROR) , out(0) , pfout(0) , use_pf(false) {} 67 68 void SetOstream(ostream* output) { 69 out = output; 70 use_pf = false; 71 } 72 void SetPfostream(pfostream* output) { 73 pfout = output; 74 use_pf = true; 75 } 76 77 template <class Type> 78 Logger& operator<<(const Type& t); 79 80 Logger& operator<<(ostream& (*f)(ostream&)) { 81 if (use_pf) { 82 if (!pfout) {return *this;} 83 *pfout << f; 84 } else { 85 if (!out) {return *this;} 86 *out << f; 87 } 88 return *this; 89 } 90}; 91 92template <class Type> 93Logger& Logger::operator<<(const Type& t) { 94 if (use_pf) { 95 if (!pfout) {return *this;} 96 } else { 97 if (!out) {return *this;} 98 } 99 if (debug_level == NONE || global_debug_level == NONE) {return *this;} 100 if (debug_level <= global_debug_level) { 101 if (use_pf) { 102 *pfout << t; 103 } else { 104 *out << t; 105 } 106 } 107 return *this; 108} 109 110template <> 111Logger& Logger::operator<<(const DEBUG_LEVEL& t) { 112 debug_level = t; 113 return *this; 114} 115 116 117 118#endif

LogStream.cpp

#SelectExpand
1#include "LogStream.hpp" 2 3using std::cout; 4using std::endl; 5 6DEBUG_LEVEL global_debug_level = ERROR; 7 8 9 10int main(int argc , char** argv) { 11 12 pfostream pfout; 13 Logger logger; 14 15 logger << ERROR; 16 logger << "This won't be printed because the logger's output is null."; 17 logger << endl; 18 19 logger.SetOstream(&cout); 20 logger << ERROR << "Printing error to cout..." << endl; 21 22 logger.SetPfostream(&pfout); 23 logger << ERROR << "Printing error using printf..." << endl; 24 25 26 return 0; 27}

And if you want to pipe your log to a file, you should be able to use SetOstream to set it to the address of an ofstream object. That works in my library at least, even if using the address of a pfostream object doesn't.

Go to: