はじめまして。
プロダクト&サービス事業部リーダーの青野です。
今日は少し古くマイナーな情報になりますが、
C++0xで追加される拡張マニピュレーターを使用した日付の書式付入出力[N2071](Apache C++ Standard Libraryから追加)の仕組み
について書こうと思います。
C++での開発経験がある人はjavaのDateFormatのように
- (任意の書式の)日付文字列からDateオブジェクトの生成
- Dateオブジェクトから(任意の書式の)日付文字列の生成
を、簡単に行いたいと、1度は悩んだことがあるのではないでしょうか。
Linux環境であれば、strptime(日付文字列 > tm構造体)、strftime(tm構造体 > 日付文字列)という便利なCの関数があり、
この関数をラップするだけでも簡単に汎用的な処理を実装することができますが、C++0xでは標準で行うことが可能になります。
Working Draft, Standard for Programming Language C++ (pp.1097-1099)
現状のC++での日付の書式付出力
現状のC++では、time_putファセットクラスを使用した日付の書式付出力のみが可能ですが、以下に示すようにやや煩雑になります。
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 | #include <ctime> #include <locale> #include <iostream> int main() { typedef std::ostreambuf_iterator<char, std::char_traits<char> > iter_type; // tm構造体の作成 std::tm tmb; tmb.tm_year = 109; tmb.tm_mon = 4; tmb.tm_mday = 27 // time_putを使用する為の準備 iter_type it(std::cout.rdbuf()); char* fmt = "%Y : %m : %d"; char* end = fmt + std::char_traits<char>::length(fmt); const std::time_put<char,iter_type>& tp = std::use_facet<std::time_put<char,iter_type> >(std::cout.getloc()); // 日付の書式付出力 it = tp.put(it, std::cout, std::cout.fill(), &tmb, fmt, end); std::cout << std::endl; return 0; } |
実行結果 2009 : 05 : 27
C++0xでの日付の書式付入出力
- 日付文字列 > tm構造体の変換
-
basic_istream<charT, traits>& >>get_time(tm構造体のポインタ, 日付書式指定)
- tm構造体 > 日付文字列の変換
-
basic_ostream<charT, traits>& <<put_time(tm構造体のポインタ, 日付書式指定)
C++0xでは、おそらく下記のコードで実行できるようになると思います。
実際のC++0xでは実装は異なると思いますが、Apache C++ Standard Libraryとこちらを元に日付の書式付入出力を簡単に紐といてみたいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <ctime> #include <locale> #include <iostream> int main() { typedef std::ostreambuf_iterator<char, std::char_traits<char> > iter_type; // tm構造体の作成 std::tm tmb; tmb.tm_year = 109; tmb.tm_mon = 4; tmb.tm_mday = 27 // 日付の書式付出力 std::cout << put_time(&tmb, "%Y : %m : %d") << std::endl; return 0; } |
time_getファセットクラスの拡張
Apache C++ Standard Libraryでは、日付の書式付入出力を行えるように、time_getファセットクラスにget、do_getメソッドが追加で実装されていて、追加されたgetメソッドで日付文字列 > tm構造体への変換が行われます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | namespace std { template <class charT, class InputIterator = istreambuf_iterator<charT> > class time_get : public locale::facet, public time_base { public: ... // extension of this implementation iter_type get (iter_type, iter_type, ios_base&, ios_base::iostate&, tm*, const char_type*, const char_type*) const; // extension of this implementation iter_type get (iter_type, iter_type, ios_base&, ios_base::iostate&, tm*, char, char) const; ... protected: ... // extension of this implementation virtual iter_type do_get (iter_type, iter_type, ios_base&, ios_base::iostate&, tm*, char, char) const; }; } |
この拡張されたtime_get、time_putファセットクラスを直接実行すればこのままでも、C++の標準の機能として日付の書式付入出力を行うことが出来ますが、実際に使うには最初に例を示したようにやや煩雑になります。
1 | basic_istream<charT, traits>& >>get_time(tm構造体のポインタ, 日付書式指定) |
1 | basic_ostream<charT, traits>& <<put_time(tm構造体のポインタ, 日付書式指定) |
上記のように簡単に実行する為の仕組みを以降で記載します。
time_get、time_put用マニピュレータクラス
time_get、time_putファセットクラスには、それぞれ専用のマニピュレータクラスが定義されています。
このマニピュレータクラスのoperator()内で、やや煩雑であったそれぞれのファセットクラスをインスタンス化し、ストリームに対して日付の書式付入出力を行う部分が実装されています。
1 2 3 4 5 6 7 8 9 | template <class charT> struct time_get_manip { std::tm *tmb_; explicit time_get_manip (std::tm *tmb); template <class Traits> void operator() (std::basic_istream<charT, Traits>&, const charT*) const; }; |
1 2 3 4 5 6 7 8 9 | template <class charT> struct time_put_manip { const std::tm *tmb_; explicit time_put_manip (const std::tm *tmb); template <class Traits> void operator() (std::basic_ostream<charT, Traits>&, const charT*) const; }; |
iomanipの拡張
Apache C++ Standard Libraryでは、実際の処理はテンプレートパラメータで指定されたマニピュレータクラスのoperator()を実行する__rw_smanipクラスが実装されています。__rw_smanipクラスはテンプレートパラメータを変更することにより、いろいろなマニピュレータクラスのoperator()を実行できる汎用的なクラスです。Apache C++ Standard Libraryでは、setw(内部では_Setw)等の標準のマニピュレータクラスもこの__rw_smanipクラスを使用しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | template <class _Functor, class _TypeT> struct __rw_smanip { explicit __rw_smanip (_TypeT __arg); __rw_smanip (_Functor __fun, _TypeT __arg); _Functor _C_fun; _TypeT _C_arg; }; template <class _CharT, class _Traits, class _Functor, class _TypeT> inline basic_istream<_CharT, _Traits>& operator>> (basic_istream<_CharT, _Traits> &__strm, const __rw_smanip<_Functor, _TypeT> &__man); template <class _CharT, class _Traits, class _Functor, class _TypeT> inline basic_ostream<_CharT, _Traits>& operator<< (basic_ostream<_CharT, _Traits> &__strm, const __rw_smanip<_Functor, _TypeT> &__man); |
__rw_smanipクラス生成ヘルパ関数の定義
tm構造体と、日付書式を指定すれば、time_get、time_putファセットクラスのoperator()を実行する__rw_smainipクラスが生成されます。この関数が実際に日付の書式付入出力を行う際に使用されることになります。
1 2 3 | template <class charT> inline std::__rw_smanip<time_get_manip<charT>, const charT*> get_time (std::tm *tmb, const charT *fmt); |
1 2 3 | template <class charT> inline std::__rw_smanip<time_put_manip<charT>, const charT*> put_time (const std::tm *tmb, const charT *fmt); |
まとめ
上記で記載したクラス、関数を使用してtime_get、time_putファセットクラスを簡単に使うことが出来るようになります。
1 | std::cout << put_time (tmb, put_fmt) << std::endl; |
※tmbは、tm構造体のポインタ
※put_fmtは日付書式文字列
上記のコードの場合、各クラス、関数は以下のように実行され、ストリームに対して日付の書式付出力が行われることになります。
- put_timeヘルパ関数によりtime_put_manipクラスをファンクタとする__rw_smanipクラスが生成される
- operator<<の定義により、__rw_smanipの_C_funメソッド(time_put_manipのoperator())が実行される
- time_put_manipのoperator()内でtime_putにより、ストリームに対して日付の書式付出力が行われる
以上内容的には浅いですが、C++0xの日付の書式付入出力の仕組みです。
他にもC++0xでは有用な機能(スレッド等)がいろいろ追加されます。C++0xのリリースが待ち遠しいですね!





8月 19th, 2009 at 11:02
[...] C++0xでの日付の書式付入出力の仕組み [...]