Manipulators

We try to define custom manipulators for our date class. We would like to use them in the following way:


#include <iostream>
#include "date1.h"

using namespace std;

int main()
{
    cout << date::us;
    date d(2001, 10, 19);
    cout << d << endl;
    cout << date::hu << d << endl;
    cout << date::de << d << endl;
    cout << date::ansi << d << endl;
    return 0;
}

The trick is to overload the right side of the operator<< with a special type. This type will be defined in class date.


#ifndef DATE_H
#define DATE_H

#include <iostream>

class date
{
public:
    enum fmt { us, de, hu, ansi };
    friend std::ostream& operator<<( std::ostream& os, date::fmt f);

        date( int y=1900, int m=1, int d=1) { set( y, m, d); }
        date( const char *s);
    date&  set_year( int y)  { year = y; return *this; }
    date&  set_month( int m) { month = m; return *this; }
    date&  set_day( int d)   { day = d; return *this; }
    int  get_year()  const { return year; }
    int  get_month() const { return month; }
    int  get_day()   const { return day; }

    //...
    
    void read( std::istream& is);
    void print( std::ostream& os) const;

private:
    void set( int y, int m, int d) 
    { 
        /* some check here */
        year = y; month = m; day = d;
    }

    int year;
    int month;
    int day;

    static fmt next_fmt;
};

std::istream& operator>>( std::istream& is, date& d);
std::ostream& operator<<( std::ostream& os, const date &d);

std::ostream& operator<<( std::ostream& os, date::fmt f);

#endif /* DATE_H */


Now we have stored the information in the static field, and the print will use it.



#include <iostream>
#include <cstring>
#include <cstdlib>
#include "date.h"

using namespace std;

date::fmt date::next_fmt = date::ansi;

ostream& operator<<( ostream& os, date::fmt f)
{
    date::next_fmt = f;
    return os;
}
void date::print( std::ostream& os) const
{
    switch ( next_fmt )
    {
    default:
    case date::ansi:
        os << "[ " << get_year() << ", " << get_month()
        << ", " << get_day()  << " ]"; break;
    case date::us:
        os << "[ " << get_month() << ", " << get_day()
        << ", " << get_year()  << " ]"; break;
    case date::de:
        os << "[ " << get_day() << ", " << get_month()
        << ", " << get_year()  << " ]"; break;
    case date::hu:
        os << "[ " << get_year() << ", " << get_month()
        << ", " << get_day()  << " ]"; break;
    }
}
date::date( const char *s)
{
//...
}
bool operator<( date d1, date d2)
{
//...
}
date& date::next()
{
//...
}
date& date::add( int n)
{
//...
}
void date::read( std::istream& is)
{
    int y, m, d;
    if ( is >> y >> m >> d )
        set( y, m , d);
}
istream& operator>>( istream& is, date& d)
{
    d.read( is);
    return is;
}
ostream& operator<<( ostream& os, const date &d)
{
    d.print( os);
    return os;
}

So far so good... It's only the print function is ugly. The problem here is that two individual concept have merged in print: the data access and the locale information.

There is a better solution where we separate them.



#ifndef DATE_H
#define DATE_H

#include <iostream>

class date
{
public:
    enum fmt { us, de, hu, ansi };
    friend std::ostream& operator<<( std::ostream& os, date::fmt f);

        date( int y=1900, int m=1, int d=1) { set( y, m, d); }
        date( const char *s);
    date&  set_year( int y)  { year = y; return *this; }
    date&  set_month( int m) { month = m; return *this; }
    date&  set_day( int d)   { day = d; return *this; }
    int  get_year()  const { return year; }
    int  get_month() const { return month; }
    int  get_day()   const { return day; }

    //...

    void read( std::istream& is);
    void print( std::ostream& os) const;

private:
    void set( int y, int m, int d) 
    { 
        /* some check here */
        year = y; month = m; day = d;
    }

    int year;
    int month;
    int day;

    static fmt next_fmt;
    static int (date::*print1st)() const;
    static int (date::*print2nd)() const;
    static int (date::*print3rd)() const;
};

bool operator<( date d1, date d2);
inline bool operator==( date d1, date d2) { return !(d1<d2 || d2<d1); }
inline bool operator!=( date d1, date d2) { return d1<d2 || d2<d1; }
inline bool operator<=( date d1, date d2) { return !(d2<d1); }
inline bool operator>=( date d1, date d2) { return !(d1<d2); }
inline bool operator>( date d1, date d2)  { return d2<d1; }

std::istream& operator>>( std::istream& is, date& d);
std::ostream& operator<<( std::ostream& os, const date &d);

std::ostream& operator<<( std::ostream& os, date::fmt f);

#endif /* DATE_H */


And here is the implementation:



#include <iostream>
#include <cstring>
#include <cstdlib>
#include "date2.h"

using namespace std;

date::fmt date::next_fmt = date::ansi;
int (date::*date::print1st)() const = date::get_year;
int (date::*date::print2nd)() const = date::get_month;
int (date::*date::print3rd)() const = date::get_day;

ostream& operator<<( ostream& os, date::fmt f)
{
    switch ( f )
    {
    default:
    case date::ansi:
        date::print1st = date::get_year;
        date::print2nd = date::get_month;
        date::print3rd = date::get_day;
        break;
    case date::us:
        date::print1st = date::get_month;
        date::print2nd = date::get_day;
        date::print3rd = date::get_year;
        break;
    case date::de:
        date::print1st = date::get_day;
        date::print2nd = date::get_month;
        date::print3rd = date::get_year;
        break;
    case date::hu:
        date::print1st = date::get_year;
        date::print2nd = date::get_month;
        date::print3rd = date::get_day;
        break;
    }
    return os;
}
void date::print( std::ostream& os) const
{
    os << "[ " 
       << (this->*print1st)() << ' '
       << (this->*print2nd)() << ' '
       << (this->*print3rd)()
       << " ]";
}

//...

void date::read( std::istream& is)
{
    // this is not perfect...
    is >> year >> month >> day;
}
istream& operator>>( istream& is, date& d)
{
    d.read( is);
    return is;
}
ostream& operator<<( ostream& os, const date &d)
{
    d.print( os);
    return os;
}