future APIのさらなる改善案

N3865 More Improvements to std::future<T>

C++14後のConcurrency TSに予定されているfuture APIの改善では、std::futurethen()というメンバ関数を追加し、非同期処理を連続的に記述できるようにしよう、という案が出ています。

今回のこの提案文書は、Boost.Thread作者のVicenteさんによって提案されたもので(つまりfuture API改善を最初に実装する人)、then()が使いにくいのでもう少しなんとかしよう、というものです。

future::then()は引数として、future<T>を受け取る関数をとります。

int main() {
    future<std::string> result =
        async([] { return 123; })
        .then([](future<int> f) {
            return to_string(f.get()) + "hoge";
        })
        .then([](future<string> f) {
            return f.get() + "fuga";
        });

    std::cout << result.get() << std::endl; // 123hogefuga
}

then()で登録した関数はfutureの準備ができたら呼ばれるので、登録した関数の中であらためてfutureの完了を待つ必要はありません。

ではなぜfutureが渡されるのかと言うと、それはfutureが正常な値の他に、エラー(例外)も同時に扱うからです。using future = variant<T, exception_ptr>;のようになってると思ってください。

つまり、then()で登録した関数の中で、正常かエラーかを判断して適切に処理しなさい、という設計になっています。

前置きが長くなりましたが、今回の提案では、既存の提案にあるthen()に加えて、next()recover()というメンバ関数を追加することが考えられています。

int main() {
    future<std::string> result =
        async([] { return 123; })
        .next([](int x) {
            return to_string(x) + "hoge";
        })
        .next([](string s) {
            return s + "fuga";
        });

    std::cout << result.get() << std::endl; // 123hogefuga
}

next()には、future<T>ではなくTを受け取る関数を登録します。エラーになったときのハンドリングには、next()に続いて、recover()を登録します。

int main() {
    future<std::string> result =
        async([] { return 123; })
        .next([](int x) {
            return to_string(x) + "hoge";
        })
        .recover([](exception_ptr) {
            std::cout << “error” << std::endl;
        });
}

正常値の場合はnext()に登録した関数が呼ばれ、エラーの場合はrecover()に登録した関数が呼ばれます。

futureに格納する値は多くの場合に正常値なので、エラーのために毎回future<T>を書くのを避けられるのはとても重要なポイントだと思います。

この提案ではその他にも、細かい改善のAPIが考えられています。

参照