Thread: C++ Tips and Tricks

  1. #1
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708

    Lightbulb C++ Tips and Tricks

    Noticing this thread, I thought it would be interesting to start one on C++ techniques. Maybe a mod could make it a sticky, as well?

    Here's the first installment: debugging macros. Very handy when you just want to see the name and value of a variable or expression. For convenience, they can be turned off by defining NDEBUG.

    Code:
    
    #ifndef BUG_HPP
    #define BUG_HPP
    
    /*	
    	A simple set of macros for printing variable/expression text and values.
    	SBUG( stream, expression ) writes output to any std::ostream-like object.
    	BUG( expression ) writes output to std::cout.
    	If NDEBUG is defined, macros expand to nothing.
    */
    
    #include <ostream> // for std::cout
    #include <cctype> // for isdigit
    
    namespace BUG_IMPL_ {
    
    template < typename Stream, typename Type >
    void print_dispatch( Stream& stream, char const* expression, Type const& value )
    {
    	stream << "*** [" << __FILE__ << ", line #" << __LINE__ << "] ***\n";
    /*
    	We'll flush the stream twice, just in case the outputting of 'value' should crash, 
    	throw an exception, etc - at least we'll know what file/line we were at before the fact
    */
    	stream.flush( );
    /*
    	Check if the expression was some sort of literal
    */
    	int
    		ch = expression[ 0 ];
    	if( !isalpha( ch ) && ch != '_' )
    	{
    	/*
    		If already quoted, output as is
    	*/
    		if( ch == '"' || ch == '\'' )
    			stream << " " << expression << "\n";
    		else
    			stream << " '" << expression << "'\n";
    	}		
    	else
    		stream << " " << expression << " = '" << value << "'\n";
    	stream.flush( );	
    }
    
    } // namespace BUG_IMPL_
    
    #ifndef NDEBUG
    #define SBUG( stream, expression ) BUG_IMPL_::print_dispatch( stream, #expression, expression );
    #define BUG( expression ) SBUG( std::cout, expression )
    #else
    #define SBUG( stream, expression )
    #define BUG( expression )
    #endif // !NDEBUG
    
    #endif // BUG_HPP
    Example usage:

    Code:
    
    
    #include <iostream>
    #include <cmath>
    
    int main( void )
    {
        BUG( "entering main" );
        int
            a = 200;
        float
            b = 0.5;
        char const*
            c = "ten";
        BUG( a );
        BUG( b );
        BUG( sqrt( a * b ) );
        BUG( c );
        BUG( "exiting main" );
    }
    Output:

    *** [test.cpp, line #62] ***
    "entering main"
    *** [test.cpp, line #69] ***
    a = '200'
    *** [test.cpp, line #70] ***
    b = '0.5'
    *** [test.cpp, line #71] ***
    sqrt( a * b ) = '10'
    *** [test.cpp, line #72] ***
    c = 'ten'
    *** [test.cpp, line #73] ***
    "exiting main"
    And don't forget, they work with user-defined types, too:

    Code:
    
    
    #include <ostream>
    #include <string>
    
    using namespace
        std;
    
    struct name
    {
        name( string const& first, string const& last )
        : first( first ), last( last )
        {    }
        
        friend ostream& operator << ( ostream& stream, name const& name )
        {
            return stream << name.last << ", " << name.first;
        }
        
        string 
            first, 
            last;
    };    
        
    int main( void )
    {
        name
            someone( "Sebastian", "Garth" );
        BUG( someone );
    }
    Output:

    *** [test.cpp, line #103] ***
    someone = 'Garth, Sebastian'
    Cheers!
    Last edited by Sebastiani; 12-19-2009 at 11:46 PM. Reason: functionality

  2. #2
    Registered User jeffcobb's Avatar
    Join Date
    Dec 2009
    Location
    Henderson, NV
    Posts
    875
    I would like to suggest having the ability to push this stuff out through a socket for remote debugging purposes......
    C/C++ Environment: GNU CC/Emacs
    Make system: CMake
    Debuggers: Valgrind/GDB

  3. #3
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by jeffcobb View Post
    I would like to suggest having the ability to push this stuff out through a socket for remote debugging purposes......
    That's essentially what the SBUG macro is for. To get it to work, you could derive your socket writer directly from the standard output streams, for example. A fairly good overview can be found here. A better approach might be to use the boost::iostreams library, though. Much more flexible, in the long run. Another option (and note that I added templated stream parameter to the print routine) is to use customized class - it would need to support the '<<' operator, of course, as well as a 'flush' member function, so that may not be the most practical approach, in general.

  4. #4
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Just thought of an easy way to do it without having to resort to using inheritance (eg: the last option I mentioned):

    Code:
    
    
    #include <sstream>
    #include <iostream>
    #include <cmath>
    
    using namespace
    	std;
    
    /*
    	The hypothetical connection class
    */
    class remote_connection
    {	
    	public:
    /*
    	Ideally, we would return the number of bytes written. For simplicity's sake, 
    	we'll just assume that the class throws an exception on error.
    */	
    	void write( char const* data, size_t length )
    	{
    		// ...send the data. We'll just write to the console for now...
    		cout.write( data, length );
    		cout.flush( );
    	}
    	
    	// ...the rest of the implementation...
    };
    
    /*
    	'Writer' just needs to implement a 'write' member function. 
    	Everything else is handled by the adapter.
    */
    template < typename Writer >
    class bug_writer_adapter
    {
    	public:
    	
    	bug_writer_adapter( Writer& writer )
    	: writer( writer )
    	{	}
    	
    	template < typename Type >
    	friend bug_writer_adapter& operator << 
    	( 
    		bug_writer_adapter& stream, 
    		Type const& value
    	)
    	{
    		stringstream
    			helper;
    		helper << value;
    		stream.data += helper.str( );
    		return stream;
    	}
    	
    	void flush( void )
    	{
    		if( !data.empty( ) )
    		{
    			writer.write( &data[ 0 ], data.size( ) );
    			data.clear( );
    		}
    	}
    	
    	protected:
    
    	Writer
    		writer;
    	string
    		data;
    };
    
    int main( void )
    {
    	remote_connection
    		test;
    	bug_writer_adapter< remote_connection >
    		brca( test );
    	SBUG( brca, "entering main" );
    	int
    		a = 200;
    	float
    		b = 0.5;
    	char const*
    		c = "ten";
    	SBUG( brca, a );
    	SBUG( brca, b );
    	SBUG( brca, sqrt( a * b ) );
    	SBUG( brca, c );
    	SBUG( brca, "exiting main" );
    }
    So the underlying remote connection class doesn't have to be modified, obviously - the adapter translates all the debugging output to text and then passes the formatted chunk of bytes to it. All and all, it's a pretty simple setup.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Tips for drawing a grid
    By countchocula in forum C Programming
    Replies: 12
    Last Post: 04-19-2008, 07:47 AM
  2. What's up with the Tips & Tricks section?
    By bikr692002 in forum C++ Programming
    Replies: 3
    Last Post: 04-05-2006, 08:04 PM
  3. Need some tips.
    By arnis in forum Game Programming
    Replies: 3
    Last Post: 03-25-2002, 01:55 PM
  4. Tips And Tricks
    By The15th in forum Windows Programming
    Replies: 7
    Last Post: 01-03-2002, 07:12 AM