1

I'm trying to make an elegant logging system in C++. I'm currently using printf(), although cout can also be an option.

What I want to achieve is something like this

console_log( "ClassName", "funcName", "Message." );

My current code for this is simply:

static void console_log( const std::string & className, const std::string & funcName, const std::string & message ) {
    printf( "%s : %s : %s\n",  className.c_str(), funcName.c_str(), message.c_str() );
}

It prints nicely, like this

// For example:
console_log( "MenuPanel", "selectedThisButton", "Something happened." );
console_log( "MenuPanel", "selectedAnotherButton", "Another thing happened." );

// Output:
MenuPanel : selectedThisButton : Something happened.
MenuPanel : selectedAnotherButton : Another thing happened.

However, I want it to be printed in a table-like manner, where all "columns" are aligned properly. For example:

MenuPanel : selectedThisButton    : Something happened.
MenuPanel : selectedAnotherButton : Another thing happened.

How do I make it so that the first and second "columns" have the exact same width/number of characters, with additional spaces if necessary? It doesn't need to be dynamic. Something like setting a "column" to 16 characters will do.

I don't wish to use any third-party libs for something as simple as this, though. And if possible, no boost.

9
  • 5
    An "elegant logging system in C++" must be the single most re-invented wheel ever. Commented Aug 23, 2013 at 8:38
  • 1
    How do you want to fix the width? If it is hardcoded, you can just add as many spaces as you need, after computing the length of the string. Commented Aug 23, 2013 at 8:39
  • 2
    Not even attempting this simple counting problem is evidence enough for me that the question should be closed. Oh, and, printf has alignment operators if one should read the man page (e.g. printf( "%-16s", ... );). Commented Aug 23, 2013 at 8:40
  • 2
    printf( "%s : %s : %s\n", className.c_str(), funcName.c_str(), message.c_str() ); should be for example: printf( "%30s : %30s : %30s\n", className.c_str(), funcName.c_str(), message.c_str() ); with whatever width you like as well you can check printf documentation for alignment modifiers Commented Aug 23, 2013 at 8:43
  • @ChronoTrigger I was hoping for a simple one-liner to format the string instead of creating a function with 10+ lines. Commented Aug 23, 2013 at 8:44

3 Answers 3

4

I'd use I/O streams instead of C-style printf and for printing stuff (of any kind) with specific width you can then include <iomanip> and set field width:

some_stream << std::setw(MY_WIDTH) << whatever;

Example:

#include <iostream>
#include <iomanip>

int main()
{
    std::cout << std::setw(10) << "hi";
}

outputs:

        hi
Sign up to request clarification or add additional context in comments.

13 Comments

Is there any reason why you prefer I/O streams over printf()?
@LanceGray: It's always better to prefer what the standard library offers over the old C-style solutions that some might find "ghastly" :)
+1 as the question is tagged C++, not because <iostream> must be better because it's in the standard library.
@LihO Honestly I think that printf() functionality gives you much elegant/readable code then cout formatting which more looks like a 'patch' to keep stream formatting within standard c++ library..
@LanceGray Maybe the fact that they actually work, without all of the risks of undefined behavior, and without having to learn a separate meta-language.
|
3

if you up to go in 'keep it simple' way you can replace:

printf( "%s : %s : %s\n", className.c_str(), funcName.c_str(), 
message.c_str() ); 

to

printf( "%30s : %30s : %30s\n", className.c_str(), funcName.c_str(), 
message.c_str() );

with whatever width you like as well you can check printf documentation for alignment modifiers.

2 Comments

Of course, this doesn't actually format like he wants. The embedded language used by printf isn't that simple.
well, I started to program C language much earlier then cout appears =) and printf is not that hard.. compare for example things like: printf( "(%0.10X) %0.10X %s\n", threadId, objectId, message ).. with an amount of code you need for cout
0

The general solution is quite complicated, but if you can assume a fixed width font (where every character has the same width), and know the width of each column up front, something like:

std::string rPad( std::string const& original, int minWidth )
{
    return original
        + std::string( 
            std::max( minWidth - static_cast<int>( original.size() ), 0 ),
            ' ' );
}

void console_log(
    std::string const& className,
    std::string const& funcName,
    std::string const& message )
{
    std::cout << rPad( className, classNameWidth )
        << " : " << rPad( funcName, funcNameWidth )
        << " : " << rPad
        << std::endl;
}

Alternatively, you can do something like:

void console_log(
    std::string const& className,
    std::string const& funcName,
    std::string const& message )
{
    std::cout << alignedText( classNameWidth ) << className
        << " : " << alignedText( funcNameWidth ) << funcName
        << " : " << message << std::endl;
}

The manipulator alignedText is generally useful, and you should have it in your toolbox anyway:

class alignedText
{
    mutable std::ostream* myOwner;
    mutable std::ostream::fmtflags myFlags;
    int myWidth;
public:
    alignedText( int width ) : myOwner myWidth( width ) {}
    ~alignedText()
    {
        if ( myOwner != nullptr ) {
            myOwner->flags( myFlags );
        }
    }
    void set( std::ostream& dest ) const
    {
        if ( myOwner == nullptr ) {
            myOwner = &dest;
            myFlags = myOwner->flags();
        }
        dest.setf( std::ios_base::left, std::ios_base::adjustfield );
        dest.width( myWidth );
    }
    friend std::ostream& operator<<( std::ostream& dest, alignedText const& manip )
    {
        manip.set( dest );
        return dest;
    }
};

(While the same thing can be done using printf, the results are a lot less readable, totally unmaintainable, and of course, you have the general fragility of printf.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.