スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

wxString::Format などを boost::format で置き換える話

今年の初めに姫踊子草で使っている wxWidgets を 2.8 系から 2.9.4 に差し替えたのはいいが wxString::Format が厳しすぎて wxLogXXX の挙動に困るという話を書きました。

選択肢としては次のものがあったのですが。

  1. 地道に全ての wxString::Format, wxString::Printf, wxLogDebug はじめ wxLogXXXX 系統のすべてをチェックする、あるいは別の方法を使って書き直す
  2. 実行時にエラーが出てしまうのを諦めて見つかり次第モグラたたき的に修正する
  3. とりあえず応急措置として動作記録をとるのはやめておく
  4. なんとか元のコードを操作しなくてすむ代理手段を探す

最初のは理想ですが現実問題多すぎて無理、二つ目はさすがに利用者に迷惑かけすぎ、しょうがないので一時的に三つ目を採用し、ようやくこのたび四つ目に成功したっぽいのでそのご報告。成果実物については前ブログエントリの版2.0013号構築40番のとおり更新済み。

具体的な代理手段というのは、表題の通り boost::format を使う。

wxString::Format も boost::format も Unix-98 形式の %1$s みたいな表記をともに採用しているのと、実行時に止めないという点では一番信用できそうだったのが採用理由。

前者は printf 同様のカンマ区切り、後者は operator%() で繋いで引数を渡すという違いはマクロやテンプレートでなんとかしましょう、ということでできたのが下記のコード。

これで呼び出し側の wxLogDebug を Dbg, wxLogMessage を Msg と置き換えていって、ついでに wxString::Format も wxToBoostFormat に置き換えてしまえばまず実行時に止まったりはしなくなります。なるはずです。

注意点は wxWidgetsのヘッダよりも先に boost/format.hpp の include を済ませておくこと。でないとデバッグビルドができなくなります。

途中で動作を止めない、という意味で重要なのは f.exceptions(boost::io::no_error_bits) 、これ広域変数とかせめて構築子で指定できたらよかったんですけど、テンプレートコードの中でしか使わないから気にしないことにする。

可変数引数関数にCでは使えないC++の諸々を渡すのは危険。しかし可変数引数マクロならば問題はない、というのがミソ。後者は元々 C++ でなくて C の機能だとかなんとか。__VA_ARGS__ のあたり。

実行速度については別途実測調査してみたところ直接 wxLogXXXX を使うときに比べて2倍から2.5倍になるみたいです。if 文でログ自体不要の場合は弾いているので、姫踊子草の設定窓口で wxLogDebug を指定したりしない限りそこまでひどいことにはならないはずですが、そもそも背に腹は代えられません。

template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0,const T1& p1,const T2& p2,const T3& p3,const T4& p4,const T5& p5,const T6& p6,const T7& p7,const T8& p8) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0%p1%p2%p3%p4%p5%p6%p7%p8; return f.str(); }
template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0,const T1& p1,const T2& p2,const T3& p3,const T4& p4,const T5& p5,const T6& p6,const T7& p7) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0%p1%p2%p3%p4%p5%p6%p7; return f.str(); }
template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0,const T1& p1,const T2& p2,const T3& p3,const T4& p4,const T5& p5,const T6& p6) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0%p1%p2%p3%p4%p5%p6; return f.str(); }
template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0,const T1& p1,const T2& p2,const T3& p3,const T4& p4,const T5& p5) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0%p1%p2%p3%p4%p5; return f.str(); }
template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0,const T1& p1,const T2& p2,const T3& p3,const T4& p4) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0%p1%p2%p3%p4; return f.str(); }
template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0,const T1& p1,const T2& p2,const T3& p3) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0%p1%p2%p3; return f.str(); }
template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0,const T1& p1,const T2& p2) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0%p1%p2; return f.str(); }
template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0,const T1& p1) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0%p1; return f.str(); }
template wxString wxToBoostFormat(const std::wstring& fmt,const T0& p0) { boost::wformat f(fmt); f.exceptions(boost::io::no_error_bits); f%p0; return f.str(); }
template wxString wxToBoostFormat(const T& s) { return s; }

// 次のマクロ群は事態の重い順で並べている
// do while(0) は波括弧なし if 文に対応するため
// tOmOp という変な名前は呼び出し側を上書きしないようにするため
#define Ftl(...) do { if (wxLog::GetLogLevel() >= wxLOG_FatalError) { const wxString tOmOp = wxToBoostFormat(__VA_ARGS__); wxLogFatalError("%s", tOmOp); } } while(0)
#define Err(...) do { if (wxLog::GetLogLevel() >= wxLOG_Error) { const wxString tOmOp = wxToBoostFormat(__VA_ARGS__); wxLogError("%s", tOmOp); } } while(0)
#define Wrn(...) do { if (wxLog::GetLogLevel() >= wxLOG_Warning) { const wxString tOmOp = wxToBoostFormat(__VA_ARGS__); wxLogWarning("%s", tOmOp); } } while(0)
#define Msg(...) do { if (wxLog::GetLogLevel() >= wxLOG_Message) { const wxString tOmOp = wxToBoostFormat(__VA_ARGS__); wxLogMessage("%s", tOmOp); } } while(0)
#define Sts(...) do { if (wxLog::GetLogLevel() >= wxLOG_Status) { const wxString tOmOp = wxToBoostFormat(__VA_ARGS__); wxLogStatus("%s", tOmOp); } } while(0)
#define Vbs(...) do { if (wxLog::GetLogLevel() >= wxLOG_Info) { const wxString tOmOp = wxToBoostFormat(__VA_ARGS__); wxLogVerbose("%s", tOmOp); } } while(0)
#define Dbg(...) do { if (wxLog::GetLogLevel() >= wxLOG_Debug) { const wxString tOmOp = wxToBoostFormat(__VA_ARGS__); wxLogDebug("%s", tOmOp); } } while(0)

単引数の wxToBoostFormat がインチキくさいですが、テンプレートだけですませるにはこれが一番簡単ですし間違ってりゃコンパイルの時点でエラーも出るし、実害ないはずなので勘弁してください。

theme : プログラミング
genre : コンピュータ

comment

Secret

リンク
RSSフィード
ブログ内検索
最近のトラックバック
月別アーカイブ
ブロとも申請フォーム

この人とブロともになる

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。