Boost logo

Boost :

From: Sven Van Echelpoel (sven.vanechelpoel.sv_at_[hidden])
Date: 2002-05-15 09:31:03


Oops!
I'm afraid my previous posting contained some inconsistencies.
Of course you can supply additional formatting parameters. I was
thinking about 3 or 4 new solutions while typing. Maybe I shouldn't have
been so hasty :-). Anyhew, here are some more thoughts. Try this syntax
for size:

fmt.format( "Enter a number between %p1 and %p2" )
                %p1( 10, Width=10,Fill='#' )
                %p2( 100, Width=10,Fill='#' );

And its implementation:

#include "stdafx.h"
#include <sstream>
#include <string>
#include <iostream>

using namespace std;

class format_param;
class manipulator;

class formatter
{
public:

 formatter& operator % ( format_param& );

 formatter& format( char const* fmt )
 {
  mResult = fmt;
  return ( *this );
 }

 string const& str()
 {
  return ( mResult );
 }

private:
 string mResult;
};

class manipulator
{
public:
 virtual void manip_stream( ostringstream & oss ) const = 0;
};

class hex_manipulator :
 public manipulator
{
public:
 void manip_stream( ostringstream& oss ) const
 {
  oss.setf( ios_base::hex, ios_base::basefield );
 }
};

class width_manipulator :
 public manipulator
{
public:
 width_manipulator( streamsize width ) :
  the_width( width ) {}

 void manip_stream( ostringstream& oss ) const
 {
  oss.width( the_width );
 }

private:
 streamsize the_width;
};

class make_width_manipulator
{
public:
 width_manipulator operator=( streamsize sz ) const
 {
  return ( width_manipulator( sz ) );
 }
};

class fill_manipulator :
 public manipulator
{
public:
 fill_manipulator( char fill ) :
  the_fill( fill ) {}

 void manip_stream( ostringstream& oss ) const
 {
  oss.fill( the_fill );
 }

private:
 char the_fill;
};

class make_fill_manipulator
{
public:
 fill_manipulator operator=( char ch ) const
 {
  return ( fill_manipulator( ch ) );
 }

private:
 char the_fill;
};

const make_width_manipulator Width;
const hex_manipulator Hex;
const make_fill_manipulator Fill;

class format_param
{
public:
 void replace( std::string& s )
 {
        
  string::size_type sidx = s.find( param_id );

  if ( string::npos != sidx )
  {
   string tmp = s.substr( 0, sidx );
   tmp += oss.str();

   if ( sidx + param_id.length() < s.length() )
   {
    tmp += s.substr( sidx + param_id.length() );
   }

   s = tmp;
  }
 }

 

protected:
 template<typename T>
 format_param( T const& val, char const* id ) :
  param_id( id )
 {
  oss << val;
 }

 template<typename T>
  format_param( T const& val, char const* id,
                manipulator const& manip ) :
  param_id( id )
 {
  manip.manip_stream( oss );
  oss << val;
 }

 template<typename T>
 format_param( T const& val, char const* id,
               manipulator const& manip1,
               manipulator const& manip2 ) :
  param_id( id )
 {
   manip1.manip_stream( oss );
   manip2.manip_stream( oss );
   oss << val;
 }

 template<typename T>
 format_param( T const& val, char const* id,
               manipulator const& manip1,
               manipulator const& manip2,
               manipulator const& manip3 ) :
 param_id( id )
 {
  manip1.manip_stream( oss );
  manip2.manip_stream( oss );
  manip3.manip_stream( oss );
  oss << val;
 }

 // More of these ctrs
        
private:
 string param_id;
 ostringstream oss;
};

class p1 : public format_param
{
public:
        template<typename T>
        p1( T const& t ) :
                format_param( t, "%p1" )
        {
        }

        
        template<typename T>
        p1( T const& t, manipulator const& manip ) :
                format_param( t, "%p1", manip )
        {
                
        }

        template<typename T>
        p1( T const& t, manipulator const& manip1,
              manipulator const& manip2 ) :
         format_param( t, "%p1", manip1, manip2 )
        {
                
        }

        template<typename T>
        p1( T const& t, manipulator const& manip1,
            manipulator const& manip2,
            manipulator const& manip3 ) :
        format_param( t, "%p1", manip1, manip2, manip3 )
        {
                
        }

        // More of these ctrs

        
};

class p2 : public format_param
{
public:
        template<typename T>
        p2( T const& t ) :
                format_param( t, "%p2" )
        {
        }

        
        template<typename T>
        p2( T const& t, manipulator const& manip ) :
                format_param( t, "%p2", manip )
        {
                
        }

        template<typename T>
        p2( T const& t, manipulator const& manip1, manipulator const& manip2 )
:
                format_param( t, "%p2", manip1, manip2 )
        {
                
        }

        template<typename T>
        p2( T const& t, manipulator const& manip1, manipulator const& manip2,
manipulator const& manip3 ) :
                format_param( t, "%p2", manip1, manip2, manip3 )
        {
                
        }

        // More of these ctrs

        
};

formatter& formatter::operator%( format_param& param )
{
        param.replace( mResult );
        return ( *this );
}

int main(int argc, char* argv[])
{
        formatter fmt;

        fmt.format( "Enter a number between %p1 and %p2" )
                %p1( 10, Width=10,Fill='#' )
                %p2( 100, Width=10,Fill='#' );

        cout << fmt.str() << endl;
        
        return 0;
}

An alternative would be to separate the formatting options like so:

fmt.format( "Enter a number between %p1 and %p2" )
  %p1( 10 ) (Hex, Width=8, Fill='0' )
  %p2( 100 );

All we have to do is add a few overloaded operator()'s:

#include "stdafx.h"
#include <sstream>
#include <string>
#include <iostream>

using namespace std;

class format_param;
class manipulator;

class formatter
{
public:

        formatter& operator % ( format_param& );

        formatter& format( char const* fmt )
        {
                mResult = fmt;
                return ( *this );
        }

        string const& str()
        {
                return ( mResult );
        }

private:
        string mResult;
};

class manipulator
{
public:
        virtual void manip_stream( ostringstream & oss ) const = 0;
};

class hex_manipulator :
        public manipulator
{
public:
        void manip_stream( ostringstream& oss ) const
        {
                oss.setf( ios_base::hex, ios_base::basefield );
        }
};

class width_manipulator :
        public manipulator
{
public:
        width_manipulator( streamsize width ) :
                the_width( width ) {}

        void manip_stream( ostringstream& oss ) const
        {
                oss.width( the_width );
        }

private:
        streamsize the_width;
};

class make_width_manipulator
{
public:
        width_manipulator operator=( streamsize sz ) const
        {
                return ( width_manipulator( sz ) );
        }
};

class fill_manipulator :
        public manipulator
{
public:
        fill_manipulator( char fill ) :
                the_fill( fill ) {}

        void manip_stream( ostringstream& oss ) const
        {
                oss.fill( the_fill );
        }

private:
        char the_fill;
};

class make_fill_manipulator
{
public:
        fill_manipulator operator=( char ch ) const
        {
                return ( fill_manipulator( ch ) );
        }

private:
        char the_fill;
};

const make_width_manipulator Width;
const hex_manipulator Hex;
const make_fill_manipulator Fill;

class format_param
{
        struct param_value
        {
                virtual format_value( ostringstream& oss ) const = 0;
        };

        template<typename T>
        struct param_value_impl :
                public param_value
        {
                param_value_impl( T const& t ) :
                        val( t ) {}

                virtual format_value( ostringstream& oss ) const
                {
                        oss << val;
                }

        private:
                T const& val;
        };

public:

        

        void replace( std::string& s )
        {
                value->format_value( oss );

                string::size_type sidx = s.find( param_id );

                if ( string::npos != sidx )
                {
                        string tmp = s.substr( 0, sidx );
                        tmp += oss.str();

                        if ( sidx + param_id.length() < s.length() )
                        {
                                tmp += s.substr( sidx + param_id.length() );
                        }

                        s = tmp;
                }
        }

        format_param& operator()( manipulator const& manip )
        {
                manip.manip_stream( oss );
                return ( *this );
        }
        format_param& operator()( manipulator const& manip1, manipulator const&
manip2 )
        {
                manip1.manip_stream( oss );
                manip2.manip_stream( oss );
                return ( *this );
        }

        format_param& operator()( manipulator const& manip1, manipulator const&
manip2, manipulator const& manip3 )
        {
                manip1.manip_stream( oss );
                manip2.manip_stream( oss );
                manip3.manip_stream( oss );
                return ( *this );
        }

        ~format_param()
        {
                delete value;
        }

        // More of these operator()...

protected:
        template<typename T>
        format_param( T const& val, char const* id ) :
                param_id( id ), value ( new param_value_impl<T>( val ) )
        {
        }

private:
        string param_id;
        ostringstream oss;
        param_value* value;
};

class p1 : public format_param
{
public:
        template<typename T>
        p1( T const& t ) :
                format_param( t, "%p1" )
        {
        }

        
};

class p2 : public format_param
{
public:
        template<typename T>
        p2( T const& t ) :
                format_param( t, "%p2" )
        {
        }

};

formatter& formatter::operator%( format_param& param )
{
        param.replace( mResult );
        return ( *this );
}

int main(int argc, char* argv[])
{
        formatter fmt;

        fmt.format( "Enter a number between %p1 and %p2" )
                %p1( 10 ) (Width=10,Fill='#' )
                %p2( 100 ) (Width=10,Fill='#' );

        cout << fmt.str() << endl;
        
        return 0;
}

Or as in my original posting:

fmt.format( "Enter a number between %p1 and %p2" )
  %p1( 10 ) [Hex][Width=8][Fill='0']
  %p2( 100 );

As you can see there is great freedom in the syntax, with each variant
being a bit more arcane than the previous ones. Maybe things are getting
out of control. I'm also running into problems when I put everyting in a
namespace.

Still, all of this was a nice exercise.

Svenne.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk