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;
}