Skip to main content
GameDev.net gamedev.net
🔒 Locked

formatting with C++

Started by DMINATOR Dec 9, 2005 at 7:49 PM 6 replies 1.4k views
Original Post
DMINATOR
DMINATOR
Hi I really wish I could find the answer myself, but I couldn't get it from nowhere. So you guys are my only hope :(. Well I have one class and I want it to accept some kind of string and output it either to file,console or both.(Something like log):
Quote:
//this is good choice - but not actually working with string rather with char* //and causes nasty exception when string is inserted as char*. // off course I can use c_str() - But I am looking for C++ way. log.Write("Data is = %s", data.Status()); //another way // This is a good way, but I can't seems to get it working log << "Data is =%s" << data.Status(); //and this method also - good but not working log.Write("Data is =",data.Status() ); //or log.Write("Data is =" << data.Status() );
Actually each log.Write should call my function pointer, which does additional actions with message, depending from selected mode. I have problem with overloading << operator, and with other possible versions. I want to call my function once the data is parsed into string, or somewhere else. But as I understand << works multiple times passing previous result to other argument. Thank you I hope some one can help me out with this.
illiyada
illiyada
If the only method your log class has is Write( const char* fmt... ) then the only option you have is
  log.Write("Data is = %s", data.Status().c_str());


However, if you can change the log class -- introduce some kind of streaming operators (like operator<<) and/or take a look to boost::format library.
DMINATOR
DMINATOR
I am not going to use boost.

The thing is that I want to have << method or others but not C style.

The problem with << is that I want it to run my function pointer, once all parts are parsed. Here is current not working version:

   template <class T>   std::ofstream& operator << (T& obj)   {	   	   _fout <<  obj;	   //I need to call the function pointer         //and pass the string	   (this->*out)(_fout);	   return _fout;	      }
Enigma
Enigma
Quote:
Original post by DMINATOR
I am not going to use boost.

Why not? Why reinvent the wheel when you don't have to. You don't say "I'm not going to use the standard library", do you.

Anyway. Assuming that I'm correct in understanding that you want:
log << "Data is = " << data.Status();log << "Data is still = " << data.Status();

to have the effect:
std::stringstream ss1;ss1 << "Data is = " << data.Status();some_member_function_pointer_call(ss1.str());std::stringstream ss2;ss2 << "Data is still = " << data.Status();some_member_function_pointer_call(ss2.str());

You could do something like (fairly off the top of my head. There may well be better ways):
#include <iostream>#include <sstream>#include <string>class Log{		class LogLine;	public:		Log()			:			_fout(&Log::flush)		{		}		template < typename Type >		LogLine operator<<(Type const & value)		{			LogLine ll(this, _fout);			ll << value;			return ll;		}	private:		void flush(std::string const & string)		{			std::cout << string << "FLUSHED\n";		}		typedef void (Log::* LogFunction)(std::string const &);		class LogLine		{			public:				typedef void (Log::* LogFunction)(std::string const &);				LogLine(Log * log, LogFunction function)					:					log_(log),					function_(function),					flush_(true)				{				}				LogLine(LogLine & ll)					:					log_(ll.log_),					function_(ll.function_),					flush_(ll.flush_),					lineStream_()				{					lineStream_ << ll.lineStream_.str();					ll.flush_ = false;				}				~LogLine()				{					if (flush_)					{						(log_->*function_)(lineStream_.str());					}				}				template < typename Type >				LogLine & operator<<(Type const & value)				{					lineStream_ << value;					return *this;				}			private:				Log * log_;				LogFunction function_;				bool flush_;				std::ostringstream lineStream_;				LogLine & operator=(LogLine const & ll);		};		LogFunction _fout;};int main(){	Log log;	log << 1 << 2 << 3;	log << "This is a line";	log << "Hello " << std::string("number ") << 5;}

Enigma
DMINATOR
DMINATOR
Quote:
Original post by Enigma
Quote:
Original post by DMINATOR
I am not going to use boost.

Why not? Why reinvent the wheel when you don't have to. You don't say "I'm not going to use the standard library", do you.

Anyway. Assuming that I'm correct in understanding that you want:
log << "Data is = " << data.Status();log << "Data is still = " << data.Status();

to have the effect:
std::stringstream ss1;ss1 << "Data is = " << data.Status();some_member_function_pointer_call(ss1.str());std::stringstream ss2;ss2 << "Data is still = " << data.Status();some_member_function_pointer_call(ss2.str());

You could do something like (fairly off the top of my head. There may well be better ways):
*** Source Snippet Removed ***
Enigma


Thank you for your advice. About boost, well first I don't want to include additional libraries files with my code, secondly I can't understand a thing how to install this boost . About the wheel - well I can stop programming, or maybe use some already made game engines, OGRE for example. Why bother everything is already made.

About the code, I don't understand it either, it is highly complicated for such a simple task (?).

Here is how I am currently using C style format:
_lbuff is just char[500];

//and out is just a function that accepts string as argument
typedef void (Log::*__Out)(const std::string &message);
__Out out;


void Log::Write(const char *msg, ...){   va_list args;   va_start(args,msg);   vsprintf(_lbuff,msg,args);   va_end(args);   //now send it to our message   (this->*out)(_lbuff);}




Enigma
Enigma
Quote:
Original post by DMINATOR
Thank you for your advice. About boost, well first I don't want to include additional libraries files with my code, secondly I can't understand a thing how to install this boost.

The only really valid reasons for not wanting to add additional libraries are:
  • cross-compiler/cross-platform support
  • constantly moving development between (new) machines such that libraries would have to be installed frequently
  • not wanting other developers to have to download additional files to use your code

Boost is sufficiently well-known and portable that I don't consider the first or last of those reasons to apply to it. That leaves only situations where development is constantly moving machines, which is a pretty rare case. As to how to install Boost, well for most of the libraries it's a case of:
  • Download Boost
  • Extract Boost
  • Set up compiler include path for Boost

Which is about as easy as it gets. For a few of the libraries you have the additional steps:
  • Download Boost.Jam
  • run bjam "-sTOOLS=..." where ... is the relevent toolset for your compiler, selected from the Getting Started page.

Quote:
About the wheel - well I can stop programming, or maybe use some already made game engines, OGRE for example. Why bother everything is already made.

You could also build your own microprocessor and write your code in binary, but you don't. A ready-made engine is a high level abstraction and unless you design for that specific engine it is unlikely that it will exactly meet your requirements and will require some form of modification. In this case it may be more productive to build your own engine from scratch for reasons of poor documentation, a design which is not easily modified to your needs, etc. The standard library provides a much lower level of abstraction and it is much more likely to meet your requirements with little or no modification. Obviously you have to draw the line at some level. Boost provides a level of abstraction which tends to be only a little higher than the standard library so I view using one and not considering the other to be silly.

Quote:
About the code, I don't understand it either, it is highly complicated for such a simple task (?).

It's not that complicated. The first time you call operator<< on your log in a statement it creates an unnamed temporary object of type Log::LogLine and the value is redirected into that object. All subsequent calls to operator<< also act on that object. At the end of the statement the temporary object is destroyed and its destructor calls the member function pointer you specified. So the code:
log << "Hello " << std::string("number ") << 5;

Essentially translates to (pseudocode, not an exact translation):
{	Log::LogLine temp(log);	temp << "Hello ";	temp << std::string("number ");	temp << 5;	temp.~Log::LogLine();}

where temp.~Log::LogLine() calls (effectively) (log->*out)(temp.str());. (I now realise that I'd incorrectly copied the name of your member function pointer as _fout, not out before).

There's a little extra work involving the flush_ member variable to handle the case where the temporary object is returned by value (on compilers which don't implement the return value optimization this can result in a copy-construction and a destructor call. We don't want that destructor call to call the member function pointer).
Quote:
typedef void (Log::*__Out)(const std::string &message);

Naughty. Identifiers beginning with a double underscore are reserved for compiler and standard library writers.

Enigma
Zahlman
Zahlman
Something like this?

// First we define this helper class...class LogData {  std::stringstream ss;  Log* logger;  public:  LogData(const Log& l) : ss(), logger(&l) {}  template<typename T>  LogData& operator()(const T& next) { ss << next; return *this; }  void operator()() { logger->Write(ss.str().c_str()); }};// Now the Write function becomes something like...void Log::Write(const char* const data) {  (this->*out)(data);}// and we can use it like this:int main() {  Log l;  LogData(l)("hello ")(std::string("world!!"))(111)();}

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.