Jump to content
  • Advertisement
Sign in to follow this  
c4c0d3m0n

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

This topic is 3625 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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 .

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
#include <stdio.h>
#include <stdarg.h>

//...

void
R2e::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 scope
Process 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()?

Share this post


Link to post
Share on other sites
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;
}



Share this post


Link to post
Share on other sites
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.)

Share this post


Link to post
Share on other sites
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 );

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!