user-defined manipulator: Format

Stroustrup ["The C++ Programming Language, 4th Ed", pp 1097-1098] has provided an example of an interesting user-defined manipulator represented by the Format class.

The motivation is as follows:
Formatting is controlled by a multitude of separate flags and operations.

For example, all flags and operations exhibit “sticky” behavior (ie, they persist until explicitly changed), but a width() applies only to the next operation of certain types.

What we want is something that makes it simple to output a floating-point number in a predefined format without affecting future output operations on the stream.

Thus, we want to define the following:
a) a class that represents formats,
b) another that represents a format plus a value to be formatted and finally
c) an operator << that outputs the value to an ostream according to the format.

These abstractions are shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class Format;

/// Format plus value
struct Bound_format
{
   const Format& f;
   double val;
};

class Format
{
private:
   int prc;			/// precision

   /// width 0 means "as wide as necessary"
   int wdt;

   /// general, scientific or fixed
   std::ios_base::fmtflags fmt;

   friend std::ostream& operator<<(std::ostream&,
                                   const Bound_format&);

   ///

public:
   explicit Format(int p = 6,
                   std::ios_base::fmtflags f = {},
                   int w = 0) :
      prc {p},
      wdt {w},
      fmt {f}
   {
   }

   /// Make a Bound_format for *this and d
   Bound_format operator() (double d) const
   {
      return Bound_format {*this, d};
   }

   Format& scientific()
   {
      fmt = std::ios_base::scientific;
      return *this;
   }

   Format& fixed()
   {
      fmt = std::ios_base::fixed;
      return *this;
   }

   Format& general()
   {
      fmt = {};
      return *this;
   }

   Format& uppercase();
   Format& lowercase();

   Format& precision(int p)
   {
      prc = p;
      return *this;
   }

   Format& width(int w)
   {
      wdt = w;
      return *this;
   }

   Format& fill(char);

   Format& plus(bool b = true);		/// explicit plus

   /// print trailing zeros
   Format& trailing_zeros(bool b = true);
};



The output operator that takes a Bound_format as an argument is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::ostream& operator<<(std::ostream& os,
                         const Bound_format& bf)
{
   std::ostringstream s;

   s.precision(bf.f.prc);
   s.setf(bf.f.fmt, std::ios_base::floatfield);
   
   /// Compose string in s.
   s << bf.val;

   /// Output s to os.
   return os << s.str();
}



The folowing program tests these abstractions and works fine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "Format.h"

using namespace std;

/// general format, precision 4
Format gen4 {4};

/// scientific format, precision 8
void testsci8(const double& d)
{
   Format sci8;

   /// scientific format, precision 8
   sci8.scientific().precision(8);

   cout << d << ' ' 
        << gen4(d) << ' '
        << sci8(d) << ' '
        << d  << endl;
}

/// scientific format, precision 10
void testsci10(const double& d)
{
   /// scientific format, precision 10
   Format sci {10, std::ios_base::scientific};

   cout << d << ' ' 
        << gen4(d) << ' '
        << sci(d) << ' '
        << d  << endl;
}



I seek guidance on how to define the functions declared in the Format class, viz:

1
2
3
4
5
6
7
8
9
10
   Format& uppercase();

   Format& lowercase();

   Format& fill(char);

   Format& plus(bool b = true);		/// explicit plus

   /// print trailing zeros
   Format& trailing_zeros(bool b = true);


I have tried a few things for uppercase(), but they don't work. Hence the query.

Thanks.
I have tried a few things for uppercase(),

What have you tried?

but they don't work.

Why didn't they work?

Post some code illustrating what you've tried and ask questions about the posted cod.
This is what I tried:

1
2
3
4
5
6
7
8
9
10
11
   Format& uppercase()
   {
      fmt = std::ios_base::uppercase;
      return *this;
   }

   Format& lowercase()
   {
      fmt = ~std::ios_base::uppercase;
      return *this;
   }


However, when I invoke the uppercase() function, a number in scientific-format (eg 1.234e002) isn't printed as 1.234E002.

Perhaps, the iostream's setf() function must be used, instead of the uppercase flag, but this function is not available as a static member of ios_base.

Any tips?

Thanks.
Did you also write a test function to test these Format specifiers?

Yes, here's the tester in full:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>			/// cout

#include "Format.h"

#define PR(X) cout << #X << " = " << X;
#define PRC(X) cout << X;
#define PRL cout << endl;

using namespace std;

/// general format, precision 4
Format gen4 {4};


/// scientific format, precision 8
void testsci8(const double& d)
{
   Format sci8;

   /// scientific format, precision 8
   sci8.scientific().precision(8);

   cout << d << ' ' 
        << gen4(d) << ' '
        << sci8(d) << ' '
        << d  << endl;
}


/// scientific format, precision 10
void testsci10(const double& d)
{
   /// scientific format, precision 10
   Format sci {10, std::ios_base::scientific};

   cout << d << ' ' 
        << gen4(d) << ' '
        << sci(d) << ' '
        << d  << endl;
}


/// test uppercase and lowercase
void testcase(const double& d)
{
   Format sci3;

   /// scientific format, precision 3
   sci3.scientific().precision(3).uppercase();

   cout << d << ' ' 
        << sci3(d) << ' '
        << d  << endl;
}


int main()
{
   PRL; PRL;
   PRC("TestFormat **");
   PRL;

   double d {1234.56789};

   testsci8(d);
   testsci10(d);
   testcase(d);
}


The testcase() function doesn't work. As I had posted earlier, a scientific-format doesn't display uppercase "E".

Actually, the function uppercase() should have set the format flag ios_base::uppercase. It doesn't.

The ios.setf(f) function, if used instead, would set an _additional_ passed flag. However, it can't be invoked as ios_base::setf(f);

I could try changing the approach entirely - by (perhaps) passing an ostream object to the functions, for which I could then invoke setf(f).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <sstream>

class Format
{
    private:
        int p = 0 ;
        int w = 0 ;
        char f = ' ' ;
        std::ios_base::fmtflags floatfld {} ;
        std::ios_base::fmtflags ucase {} ;
        std::ios_base::fmtflags showpos {} ;

        struct Bound_format { const Format& f ; const double val ; };
        friend std::ostream& operator<< ( std::ostream&, const Bound_format& );

    public:
       explicit Format( int p = 6, std::ios_base::fmtflags f = {}, int w = 0 )
           : p(p), w(w), floatfld( f & std::ios_base::floatfield ),
             ucase( f & std::ios_base::uppercase ), showpos( f & std::ios_base::showpos ) {}

       Bound_format operator() (double d) const { return { *this, d } ; }

       Format& scientific() { floatfld = std::ios_base::scientific ; return *this ; }
       Format& fixed() { floatfld = std::ios_base::fixed ; return *this ; }
       Format& general() { floatfld = {} ; return *this ; }
       Format& uppercase() { ucase = std::ios_base::uppercase ; return *this ; }
       Format& lowercase() { ucase = {} ; return *this ; }
       Format& plus( bool b = true )
       {
           if(b) showpos = std::ios_base::showpos ;
           else showpos = {} ;
           return *this ;
       }

       Format& precision( int p ) { Format::p = p ; return *this ; }
       Format& width( int w ) { Format::w = w ; return *this ; }
       Format& fill( char c ) { Format::f = c ; return *this ; }
};

std::ostream& operator<<( std::ostream& os, const Format::Bound_format& bf )
{
   std::ostringstream s;
   s.precision( bf.f.p );
   s.setf( bf.f.floatfld, std::ios_base::floatfield);
   s.setf( bf.f.ucase | bf.f.showpos ) ;
   s.width( bf.f.w ) ;
   s.fill( bf.f.f ) ;
   s << bf.val;

   return os << s.str();
}

int main()
{
    const double d = 123456.789 ;

    Format fmt ;

    std::cout << fmt.scientific().precision(3)(d) << '\n' ;
    std::cout << fmt.width(16).fill('*').uppercase().plus()(d) << '\n' ;
    std::cout << fmt.lowercase().plus(false).fill(' ').width(0)(d) << '\n' ;
    std::cout << fmt.plus().fixed().precision(6).fill('~')(d) << '\n' ;
}

http://coliru.stacked-crooked.com/a/b35b146191a8e492
Neat solution, Borges.

You have used additional fmtflags fields to get this done.

One question though - is it possible to use a _single_ fmtflags field to store all the flags? Since fmtflags is a bitmask field, this should be possible. Then using masking, we could isolate and extract each separate flag, check its status and format the ostringstream separately. I think, theoretically, this should be possible, right?

However, I like the simplicity of the approach adopted by you.

Thanks.
> is it possible to use a _single_ fmtflags field to store all the flags?
> theoretically, this should be possible, right?

Yes. For instance, single_fmtflags & std::ios_base::floatfield would give us the equivalent of the member
std::ios_base::fmtflags floatfld in the above code.

That is what we may do if it was production code; though manipulators are typically objects with short life-times, and this attempt to reduce the footprint by a few bytes (at the cost of a bit more complexity in the code) may not be worthwhile.
I agree. A simpler approach is better. To quote James Martin, "Great engineering is simple engineering."


though manipulators are typically objects with short life-times


A user-defined manipulator needn't be a one-time exercise.

If it is useful, it would be defined as a utility function in a header file (which perhaps contains other user-defined manipulators) and reused.
That the type itself (the user-defined manipulator) can be reused over and over again does not imply that objects of that type which are created must necessarily have long life times.

Though this is certainly possible: static const auto sw8 = std::setw(12) ; it would be rare.
The typical usage would be where the life-time of the manipulator object would be the duration of the execution of a single (output) statement.
Last edited on
Topic archived. No new replies allowed.