最終更新日時: 2026年07月22日 16時08分23秒 (JST)
rotarymars が更新

履歴 編集

customization point object
<execution>

# std::execution::into_variant(C++26)

    namespace std::execution {
      struct into_variant_t { unspecified };
      inline constexpr into_variant_t into_variant{};
    }

## 概要

`into_variant`は、複数の値完了シグネチャを持つ入力Senderから複数`tuple`型からなる`variant`型の値完了シグネチャへと変換するSenderアダプタである。

`into_variant`はパイプ可能Senderアダプタオブジェクトであり、パイプライン記法をサポートする。

## 効果

説明用の式`sndr`に対して`decltype((sndr))`が`sender`を満たさないとき、呼び出し式`into_variant(sndr)`は不適格となる。

そうでなければ、呼び出し式`into_variant(sndr)`は下記と等価。

    make-sender(into_variant, {}, sndr)

### Senderアルゴリズムタグ `into_variant`

Senderアルゴリズム動作説明用のクラステンプレート`impls-for`に対して、下記の特殊化が定義される。

    namespace std::execution {
      template<>
      struct impls-for<into_variant_t> : default-impls {
        static constexpr auto get-state = see below;
        static constexpr auto complete = see below;

        template<class Sndr, class... Env>
        static consteval void check-types() {
          auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
          decay-copyable-result-datums(cs);
        }
      };
    }

`impls-for<into_variant_t>::get-state`メンバは、下記ラムダ式と等価な関数呼び出し可能なオブジェクトで初期化される。

- 子Senderの値完了シグネチャを集約した`variant<tuple<...>, ...>`型を`type_identity`クラステンプレートのパラメータに格納して返す。

    []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept
      -> type_identity<value_types_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>> {
      return {};
    }

`impls-for<into_variant_t>::complete`メンバは、下記ラムダ式と等価な関数呼び出し可能なオブジェクトで初期化される。

- `State`として渡される前述`get-state`メンバ戻り値型(`type_identity`)から、`into_varinat`の送信値となる`variant<tuple<...>, ...>`型情報を取り出す。
- 値完了の場合、引数パック`args...`から送信値を構築して接続先Receiverの値完了関数を呼び出す。
- エラー完了または停止完了の場合、接続先Receiverの同種完了関数に全引数を転送する。

    []<class State, class Rcvr, class Tag, class... Args>(
        auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {
      if constexpr (same_as<Tag, set_value_t>) {
        using variant_type = typename State::type;
        TRY-SET-VALUE(rcvr, variant_type(decayed-tuple<Args...>{std::forward<Args>(args)...}));
      } else {
        Tag()(std::move(rcvr), std::forward<Args>(args)...);
      }
    }

## カスタマイゼーションポイント

Receiver接続時に、関連付けられた実行ドメインに対して`execution::transform_sender`経由でSender変換が行われる。 デフォルト実行ドメインでは無変換。

## 例

    #include <string>
    #include <tuple>
    #include <print>
    #include <variant>
    #include <execution>
    #include <utility>
    namespace ex = std::execution;

    struct FizzBuzzSender {
      using sender_concept = ex::sender_tag;

      // FizzBuzzSenderは2種類の値完了シグネチャを持つ
      using completion_signatures = ex::completion_signatures<
        ex::set_value_t(int),
        ex::set_value_t(std::string)
      >;
      template <typename Self>
      static consteval auto get_completion_signatures()
      {
        return completion_signatures{};
      }

      // Operation State型
      template <typename Rcvr>
      struct State {
        using operation_state_concept = ex::operation_state_tag;

        State(Rcvr rcvr, int val)
          : rcvr_{std::move(rcvr)}, val_{val} {}

        void start() noexcept {
          using namespace std::string_literals;
          if (val_ % 15 == 0) {
            ex::set_value(std::move(rcvr_), "FizzBuzz"s);
          } else if (val_ % 3 == 0) {
            ex::set_value(std::move(rcvr_), "Fizz"s);
          } else if (val_ % 5 == 0) {
            ex::set_value(std::move(rcvr_), "Buzz"s);
          } else {
            ex::set_value(std::move(rcvr_), val_);
          }
        }

        Rcvr rcvr_;
        int val_;
      };

      template <typename Rcvr>
      auto connect(Rcvr rcvr) noexcept {
        return State{std::move(rcvr), val_};
      }

      int val_;
    };

    template<typename... Ts>
    struct overload : Ts... { using Ts::operator()...; };

    int main()
    {
      int val = 15;
    #if 0
      // 関数呼び出し
      ex::sender auto snd0 = FizzBuzzSender{val};
      ex::sender auto sndr = ex::into_variant(snd0);
    #else
      // パイプライン記法
      ex::sender auto sndr = FizzBuzzSender{val} | ex::into_variant();
    #endif
      auto [result] = std::this_thread::sync_wait(sndr).value();

      // result := variant<tuple<int>, tuple<string>>型
      std::visit(overload{
        [](std::tuple<int> n) {
          std::println("(int) {}", get<0>(n));
        },
        [](std::tuple<std::string> s) {
          std::println("(str) {}", get<0>(s));
        }
      }, result);
    }

### 出力

    (str) FizzBuzz

## バージョン

### 言語

- C++26

### 処理系

- Clang: ??
- GCC: ??
- ICC: ??
- Visual C++: ??

## 関連項目

- `execution::when_all_with_variant`
- `this_thread::sync_wait_with_variant`

## 参照

- P2300R10 `std::execution`
- P3557R3 High-Quality Sender Diagnostics with Constexpr Exceptions
- LWG 4203. Constraints on `get-state` functions are incorrect
- P3826R5 Fix Sender Algorithm Customization