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