Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C++でもRustのResultが使いたい

 C++でもRustのResultが使いたい

C++MIX #4の発表スライド

Cranberries

June 26, 2019
Tweet

More Decks by Cranberries

Other Decks in Technology

Transcript

  1. クラス宣言 enum class mutability: bool { mut = false, immut

    = true, }; template <mutability, // mutability specification class = std::monostate, // success type class = std::monostate, // failure type class = decltype(nullptr) // for detection idiom > class basic_result;
  2. ミュータビリティ  ミュータビリティが型に埋め込まれいる(正直やりすぎ感ある)  内部で保持する値へのアクセスの制御に使われている  利用者はシノニムを利用する /// alias template

    for immutable result template <class T = std::monostate, class E = std::monostate> using result = basic_result<mutability::immut, T, E>; /// alias template for mutable result template <class T = std::monostate, class E = std::monostate> using mut_result = basic_result<mutability::mut, T, E>;
  3. resultの構築  success, failureというfactoryクラスから作れる  関数からresultを返す場合も result<str, str> res(success<str>{"foo"}); result<str,

    str> res(failure<str>{"foo"}); auto func(bool b) -> result<str, str> { if (b) { return success{"foo"}; } else { return failure{"bar"}; } }
  4. is_ok(), is_err()  それぞれ正常値・異常値を保持しているかをboolで返す result<str, str> res(success<str>{"foo"}); res.is_ok(); // true

    res.is_err(); // false result<str, str> res(failure<str>{"foo"}); res.is_ok(); // false res.is_err(); // true
  5. ok(), err()  それぞれ正常値・異常値をboost::optionalで取得できる result<int, int> res(success{42}); res.ok(); // Some(42)

    res.err(); // None result<int, int> res(failure{42}); res.ok(); // None res.err(); // Some(42)
  6. unwrap(), unwrap_err()  それぞれ正常値・異常値を取り出す  値を持っていなければ例外を送出する result<int, int> res(success{42}); res.unwrap();

    // 42 res.unwrap_err(); // raise the exception `mitama::runtime_panic` result<int, int> res(failure{42}); res.unwrap(); // raise the exception `mitama::runtime_panic` res.unwrap_err(); // 42
  7. unwrap_or_else(F func) F: E -> T  正常値を取り出す  異常値errを持っていなければfunc(err)を返す

     遅延評価したいときに使う  引数なしで呼べる場合はなしで呼ぶ result<int, str> res(success{42}); res.unwrap_or_else([](int err){ return err.length(); }); // 42 result<int, int> res(failure{42}); res.unwrap_or_else([]{ return 57; }); // 57
  8. expect(string_view msg)  unwrap()の亜種で例外のメッセージに含める文字列を指定できりゅ result<int, int> res(failure{42}); auto ok =

    res.expect("foo"); // panicked at 'foo: failure(42)' result<int, int> res(failure{42}); auto ok = res.unwrap(); // panicked at 'called `result::unwrap()` on a value: failure(42)'
  9. expect_err(string_view msg)  unwrap_err()の亜種で例外のメッセージに含める文字列を指定できりゅ result<int, int> res(success{42}); auto err =

    res.expect_err("foo"); // panicked at 'foo: success(42)' result<int, int> res(success{42}); auto err = res.unwrap_err(); // panicked at 'called `result::unwrap_err()` on a value: success(42)'
  10. map(op) op: T -> U  正常値okを持っているとき場合、op(ok) を適用しresult<U, E>として返す 

    異常値を持っていた場合はそれをresult<U, E>として返す result<int, int> res(success{42}); res.map([](auto ok){ return ok + 1; }); // success(43) result<int, int> res(failure{42}); res.map([](auto ok){ return ok + 1; }); // failure(42)
  11. map_err(op) op: E -> F  異常値errを持っているとき場合、op(err) を適用しresult<T, F>として返す 

    正常値を持っていた場合はそれをresult<T, F>として返す result<int, int> res(success{42}); res.map_err([](auto err){ return err - 1; }); // success(42) result<int, int> res(failure{42}); res.map_err([](auto err){ return err - 1; }); // failure(41)
  12. and_then(op) op: T -> result<U, E>  正常値を持っているとき、更に失敗する可能性のある関数へチェインする result<int, int>

    res(success{41}); res.and_then([](auto ok) -> result<int, int> { if ( ok % 2 == 0 ) return success{ok+1}; else return failure{ok}; // ここを通る }); // failure(41)
  13. or_else(op) op: E -> result<T, F>  異常値を持っているとき、更に失敗する可能性のある関数へチェインする result<int, int>

    res(failure{42}); res.or_else([](auto ok) -> result<int, int> { if ( ok % 2 == 0 ) return success{ok+1}; else return failure{ok}; }); // success(43)
  14. 参照のResult  mutabilityは保持する値のアクセスでも伝搬する str hoge = "foo"; mut_result<str&, str&> res(success<str&>{hoge});

    res.unwrap() = "bar"; // now hoge == "bar" result <str&, str&> res(success<str&>{hoge}); // Error: immutable result’s unwrap returns const reference res.unwrap() = "bar";
  15. indirect()  要素型をデリファレンスした型の参照のResultを作る  result<T*, E*> => result<T, E> 

    (T ok, E err) に対して { *ok, *err } という操作が要求されりゅ using vec_iter = typename std::vector<int>::iterator; std::vector<int> vec{1, 2, 3}; mut_result<vec_iter, vec_iter> res(success<vec_iter>{vec.begin()}); auto indirect = res.indirect(); // indirect: `mut_result<int&, int&>` auto& ref = indirect.unwrap(); ref = 42;
  16. indirect_ok()  success型をデリファレンスした型の参照のResultを作る  result<T*, E> => result<T, E> 

    (T ok) に対して { *ok } という操作が要求されりゅ using vec_iter = typename std::vector<int>::iterator; std::vector<int> vec{1, 2, 3}; mut_result<vec_iter, int> res(success<vec_iter>{vec.begin()}); auto indirect = res.indirect_ok(); // indirect: `mut_result<int&, int>` auto& ref = indirect.unwrap(); ref = 42;
  17. indirect_err()  failure型をデリファレンスした型の参照のResultを作る  result<T, E*> => result<T, E> 

    (E err) に対して { *err } という操作が要求されりゅ using vec_iter = typename std::vector<int>::iterator; std::vector<int> vec{1, 2, 3}; mut_result<int, vec_iter> res(failure<vec_iter>{vec.begin()}); auto indirect = res.indirect_err(); // indirect: `mut_result<int&, int>` auto& ref = indirect.unwrap(); ref = 42;
  18. dangling<T>  右辺値のresultの内部を参照した場合  dangling<std::reference_wrapper<T>>( dangle_ref )ができる using vec =

    std::vector<int>; mut_result<vec, vec>(success{vec{1, 2, 3}}) .as_mut(); // returns `mut_result<dangle_ref<vec>, dangle_ref<vec>>`
  19. dangling::transmute()  参照が生きていることを確信する場合は明示的に中身を取り出す using vec = std::vector<int>; vec v{1, 2,

    3}; mut_result<vec&, vec&>(success<vec&>{v}) .unwrap() // `mut_result<dangle_ref<vec>, dangle_ref<vec>>` .transmute(); // get `vec&`