Sign in to follow this  

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

This topic is 3317 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
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

This topic is 3317 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this