[C++] Adding printf() like functionality to my log class

Started by
5 comments, last by c4c0d3m0n 15 years, 5 months ago
class Log
{
  public:

    Log();

    void write( const std::string& );
    void save( const std::string& ) const; 


  private:

    std::stringstream m_log;

};

I would like to expand Log::write()'s functionality by adding functionality also available in printf() and the likes, so that I can use it as such:
R2e::log foobar;
float my_float = 42.23f;
foobar.write( "Value of my_float is: %d", my_float );
Can someone please guide me on how to do this? I tried Google, without success.
Advertisement
Maybe have a look at boost::format or use a more stream-based approach and just expose a std::ostream from your logger. For instance, my own logging system has the following method:

std::ostream &Log( const std::string &ChannelName );


Which you might use like this:

Log("Resource") << "Adding resource " << pResource->GetName() << std::endl;


So your code calling the Log method has no need to know what is being done with the data it's logging and you can redirect different logging 'channels' at runtime .
The feature you are looking for is called variable argument lists. Here is how to use it:

void Write(LogInfo Info, LogType Type, char *Message, ...);


The implementation:
va_list args; va_start(args, Message);char szBuf[1024];vsprintf_s(szBuf, 1024, Message, args);


The szBuf is now the string to write to the file/window/whatever.
I support only the printf way, because my messages to the logfile have a time and type assigned to it. If you do it the stream way, you can't tell what the message is exactly, or you'll have to do it like:

Log("LogFile.txt") << "This is a sample log: " << IntegerToLog << LogType << EndOfMessage;

I hope you have something from this reply.

Emiel1

PS: Always remember to flush the file you are writing for after the real write. It's a real pain to look for the reason for a crash, while the message was still in OS-memory, and not yet written to the log.

PS2: Also, you write a float with %f instead of %d.
#include <stdio.h>#include <stdarg.h>//...voidR2e::Log::operator() ( const std::string& input, ... ){    va_list args;    va_start( args, input );    char buffer[1024];    vsprintf_s( buffer, 1024, input.c_str(), args );    std::string to_log( buffer );    to_log += "\n";    m_log << to_log;    std::clog << to_log;}


-------------- Build: Debug in R2e ---------------Compiling: R2e/log.cpp/home/rob/Development/.codeblocks/R2e/R2e/log.cpp: In member function ‘void R2e::Log::operator()(const std::string&, ...)’:/home/rob/Development/.codeblocks/R2e/R2e/log.cpp:45: error: ‘vsprintf_s’ was not declared in this scopeProcess terminated with status 1 (0 minutes, 0 seconds)1 errors, 0 warnings


I decided to use the operator() to try and get this to work. What do I need to include for vsprintf_s()?
Quote:Original post by emiel1
I support only the printf way, because my messages to the logfile have a time and type assigned to it. If you do it the stream way, you can't tell what the message is exactly, or you'll have to do it like:

Log("LogFile.txt") << "This is a sample log: " << IntegerToLog << LogType << EndOfMessage;
No.

  #include <iostream>  #include <ostream>  #include <sstream>  template<typename F>  struct output_message  {    F functor;    std::stringstream target;    bool used;    output_message(const F &functor) : functor(functor), used(false) {}    output_message(const output_message &o) : functor(o.functor), used(false) {}    template <typename T> std::ostream &operator<<(const T& t)    {      used = true;      return target << t;    }    ~output_message()    {      if (used)        functor(target.str());    }  };


This allows you to do things like:

  void to_stdout(const std::string &str)  {    std::cout << "{" << str << "}\n";  }  int main()  {    // Prints "{Hello world! 42}"    print(to_stdout) << "Hello World! " << 42;  }


Quote:Original post by c4c0d3m0n
I decided to use the operator() to try and get this to work. What do I need to include for vsprintf_s()?
This is a Microsoft extension. On GCC you may want to try vsnprintf or vaprintf instead, or simply write directly to a FILE * without a buffer with plain vprintf. Oh, and don't may want to change the format to a character pointer and declare the function with "__attribute__((format(printf,1,2)))" to get format checking (possibly with an #ifdef __GNUC__ around it for portability.)
Thanks! Using vsprintf( buffer, input.c_str(), args ); made it work. Now my log works like a charm! R2e::log( "Setting up screen (%ix%i)", screenw, screenh );

This topic is closed to new replies.

Advertisement