lix::Failable
lix::Succession が失敗したときにエラーコードを返す、というだけのものであるのに対して、何らかの値を取得し、失敗時はエラーコードを返す、という場合に使用することを想定したクラステンプレートです。
// 何らかの文字列を引数で受けた string に格納して返す、失敗時は false を返し、その理由は GetLastError で取得できる。 bool GetSomeText( std::string& ret );
というシグネチャ(ここでは『失敗理由はGetLastErrorで』ということも意味的なシグネチャと捉えます。)が
lix::WinFailable<std::string> GetSomeText();
になります。Failable は boost::optional っぽい形式で内容にアクセスできます。
lix::WinFailable<std::string> GetText( int i ) { if( i < 0 ){ // ホントはなんか API を呼ぶ、その副作用で LastError が変わる。 SetLastError( ERROR_INVALID_PARAMETER ); return lix::Failed(GetLastError()); } return "てきすと"; } void sampleA(){ lix::WinFailable<std::string> r = GetText( 1 ); assert( r ); cout << r << endl; // これは内容が流れ、 "てきすと" と表示される。 std::string content = *r; // 値はこうやってとる。 content = r.Get(); // これでもいいけど . と -> の両方があるので、 // 混乱しそう。成功時はとっとと * したほうがいいと思う・ r = GetText(-1); // if( r.IsFailed()) でも同じ if( !r ){ // 失敗。 // エラーコードを返す。 DWORD err = r.GetErrorCode(); // メッセージを返す。 string message = r.GetErrorMessage(); // ストリームにも流せたりする。(これは内容ではなくエラーメッセージが流れる) cerr << r << endl; } r = GetText(-1); std::string x = r.GetValueOr("でふぉると"); // 成功してたら内容を、失敗してたら引数を返す。 cout << x << endl; // この表示は "でふぉると" }
lix::WinFailable
template< typename T, typename ErrorCodeType, typename ErrorCategory = ErrorCodeTraits<ErrorCodeType> > class FailableBase { ... }; // こう書きたいんだけどね・・・ // template <typename T> // using WinFailable = FailableBase<T, DWORD, Win32ErrorCategory >; // いまのところこうせざるをえない。 template< typename T> class WinFailable : public FailableBase<T, DWORD, Win32ErrorCategory> { ... };
ErrorCategory は lix::Succeesion と同じもので、エラーコードをどうやって文字列化するか、というポリシーです。errno版とGetLastError版が存在しています。
各エラー体系に合わせて ErrorCategory を作ってあげれば、エラーコードがどんなものであっても、
auto r = HogeHoge(); if( !r ){ // エラーハンドリング // return するか、 throw するか。 } // 改めて取り出す string s = *r;
というスタイルが使用できますし、boost::optional を使用してよくやるスタイル
if( auto r = HogeHoge() ){ string s = *r; // 正常時の処理をここに。 }else{ // 異常時の処理はここに。 }
を、エラー時の情報を殺すことなく行うことが出来ます。