lix::Succession

試しに公開してみた https://code.google.com/p/lix-failable/source/browse/ こいつですが、

例えばこういう関数を作るとして、

    // 普通は失敗しない何かをする。失敗時は false を返す、失敗理由は(Win32の)GetLastError で。
    BOOL DoSomething( int x);

この 『失敗理由は GetLastError()』 というのは、概念的にはその関数のシグネチャに含まれていますが、シンタックス的にはシグネチャではありません。
かといって、ここで『ErrorCodeを返す』ように変更してしまうと、エラーハンドリングの記述が直感的ではなくなる、という問題が発生してしまいます。

    BOOL DoSomething( int x);
    
    // どこかで
    
    DWORD err = DoSomething( path );
    if( err != NO_ERROR ){  // <- この if 文が直感的じゃない。
        ...
    }   
    
    if( err ) {  // <- これ何が言いたいのかわからない、ミスか?
        ...
    }

なので、『成否を表しつつ否の場合はエラーコードを持たせることができるクラス』があれば、そのへんがすっきりするんじゃないかと。
で、作ってみたのが lix::Succession です。

これを使うと

    lix::WinSuccession DoSomething( int x)
    {
        if( x < 0 ){
            // ホントはなんか API を呼ぶ、その副作用で LastError が変わる。
            SetLastError( ERROR_INVALID_PARAMETER );

            // 失敗、 GetLastError を持たせるよ、という明示的記述。
            return lix::Failed( GetLastError());

            // こう書いても同じ
            return lix::FailedWithLastError();

            // これでも同じ
            return lix::WinSuccession( false,  GetLastError());

            // これもコンパイル通っちゃうけど、 true を許しつつこれをやめさせる方法が思いつかなかった。
            return false;
        }

        // 成功(boolからの変換コンストラクタがある。)
        // false 側を考えると、これは変換させないようにするか、ちょっと悩んだ。
        return true;

        // どっちかっていうとこう書いてほしいな。
        return lix::Succeeded();
    }
    
    void sample1(){
        lix::WinSuccession r = DoSomething( 1 );
        assert( r.IsSucceeded());
        
        r = DoSomething(-1);
        
        // if( r.IsFailed()) でも同じ
        if( !r ){
            // 失敗。
            
            // エラーコードを返す。
            DWORD err = r.GetErrorCode();
            
            // メッセージを返す。
            wstring message = r.GetErrorMessage();
            
            // ストリームにも流せたりする。(エラーメッセージが流れる)
            cerr << r << endl; 
        }
    }

こんなふうにかけます。

lix::WinSuccession というクラステンプレートは2つのテンプレートパラメータをとり、第一パラメータがエラーコードの型、第二パラメータがそのエラーコードをどうやって文字列化するか、というポリシーです。
第二引数は lix::ErrorCodeTraits というテンプレートがデフォルトパラメータになっているため、指定しなくても構いません。
つまり lix::Succession は

template < typename ErrorCodeType, typename ErrorCategory = lix::ErrorCodeTraits<ErrorCodeType> >
class Succession{  ... };

こうです。
で何度も出てきている lix::WinSuccession は DWORD と FormatMessagge をつかうポリシーでインスタンス化した lix::Succession の typedef です。

『俺のエラーコードは独自の emun だ!』とかいう場合もメッセージの取得さえ諦めればお手軽に、

    enum CustomErrorCode {
        CUSTOM_OK = 0,
        CUSTOM_INVALID_ARG = 1,
        CUSTOM_SOMETHING_HAPPENS = 2
    };

    lix::Succession<CustomErrorCode> DoHogeHoge(int x)
    {
        if( x < 0)      {   return lix::Failed(CUSTOM_INVALID_ARG);         }
        if( x > 100 )   {   return lix::Failed(CUSTOM_SOMETHING_HAPPENS);   }
        return lix::Succeeded();
    }


    void sample2 {
        auto r = DoHogeHoge(1);
        
        assert( r.IsSucceeded());
        
        r = DoHogeHoge( -1 ){
        if( ! r ){
            cerr << "失敗した。" << r.GetErrorCode() << endl.
        }
    }

こんなかんじで使えます。

そこで、エラーメッセージも対応するならば lix::ErrorCodeTraits を特殊化してしまえばよいです。

    enum CustomErrorCode {
        CUSTOM_OK = 0,
        CUSTOM_INVALID_ARG = 1,
        CUSTOM_SOMETHING_HAPPENS = 2
    };

    // CustomErrorCode について lix::ErrorCodeTraits を特殊化。
    namespace lix{
        template<>
        class ErrorCodeTraits<CustomErrorCode> : public DefaultErrorCodeTraits<CustomErrorCode>{
        public:
            static std::string GetText( code_type code){
                if( code == CUSTOM_OK ) return "OK";
                if( code == CUSTOM_INVALID_ARG ) return "ひきすうへんだよ?";
                if( code == CUSTOM_SOMETHING_HAPPENS ) return "なんかおきた";
                return "orz.";
            }
        };
    }

    lix::Succession<CustomErrorCode> DoHogeHoge(int x)
    {
        if( x < 0)      {   return lix::Failed(CUSTOM_INVALID_ARG);         }
        if( x > 100 )   {   return lix::Failed(CUSTOM_SOMETHING_HAPPENS);   }
        return lix::Succeeded();
    }

    void sample3() {
        auto r = DoHogeHoge(1000 );
        if( ! r ){
            // これで、"なんかおきた" と表示される。
            cerr <<  r << endl;
        }
    }

この CustomErrorCode のようにエラーコードの型からその扱いが一意に決まるならば、このような特殊化で対応可能ですが、現実的には int などの汎用の型を異なる体系のエラーコードとして使用していることがほとんどだと思います。
その場合は ErrorCategory 全体を作り、パラメータをすべて指定した typedef を通して使用することになるかと思います。
とりあえずサンプルとして Win32 の LastError 機構をつかう、

typedef Succession<DWORD, Win32ErrorCategory> WinSuccession;

と、 errno をつかう、

typedef Succession<errno_t, CRTErrorNoCategory> CRTSuccession;

を作ってみました。
winsock用とか HRESULT用とか、いろいろ ErrorCategory を作ってあげれば良い感じになるのではないかな、と。