0%

Introduction to Sender and Receiver

std::execution的部分繞不開Sender/Receiver,經過多次失敗之後終於寫出一個能跑的,紀錄一下

Simplest Receiver

由於Receiver的範例比Sender簡單,所以從Receiver開始,而Sender先用Just代替

1
2
3
4
5
#include <stdexec/execution.hpp>
struct recv {
using receiver_concept = stdexec::receiver_t;
};
static_assert(stdexec::receiver<recv>);

不過光是這樣一點用都沒有
至少要有有一個Callback function

1
2
3
4
5
6
7
struct Recv {
using receiver_concept = stdexec::receiver_t;

friend void tag_invoke(set_value_t, Recv&&, int v) noexcept {
std::cout << "get value: " << v << "\n";
}
};

這樣才能跟Sender做結合

1
2
auto o1 = stdexec::connect(stdexec::just(1), Recv());
stdexec::start(o1);

至於Callback參數的形式,需要從Sender那邊定義,之後會寫一個簡單的Sender

Simplest Sender

1
2
3
4
struct Send {
using sender_concept = stdexec::sender_t;
};
static_assert(stdexec::sender<Send>);

Rece類似,這邊要有一個sender_concept
不過一樣沒什麼用,最小的實現至少是這樣子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Send {
template <typename R>
struct op {
R r_;
friend void tag_invoke(stdexec::start_t, op& self) noexcept {
stdexec::set_value(std::move(self.r_), 42);
}
};

using sender_concept = stdexec::sender_t;
using completion_signatures = stdexec::completion_signatures<stdexec::set_value_t(int)>;
template <class R>
friend op<R> tag_invoke(stdexec::connect_t, Send, R r) {
return { r };
}
};

使用方式跟上面差不多

1
2
auto o2 = stdexec::connect(Send{}, Recv());
stdexec::start(o2);

先不看op的部分,在Send有兩個部分

1
using completion_signatures = stdexec::completion_signatures<stdexec::set_value_t(int)>;

這個定義皆在後面的Receiver該接受什麼類型的參數
對照Recv

1
2
3
struct Recv {
friend void tag_invoke(set_value_t, Recv&&, int v) noexcept {}
};

兩個需要成對,不然connect的部分會出錯
connect的階段,演算法會呼叫

1
2
3
friend op<R> tag_invoke(stdexec::connect_t, Send, R r) {
return { r };
}

tag_invoke的地方不細說,由於我們不知道真正的Receiver類型是什麼,所以需要一個template版本的op
這邊也只有將SenderReceiver連接起來,還沒開始執行
執行的部分在

1
stdexec::start(o2);

演算法這時候就會呼叫

1
2
3
4
5
6
7
template <typename R>
struct op {
R r_;
friend void tag_invoke(stdexec::start_t, op& self) noexcept {
stdexec::set_value(std::move(self.r_), 42);
}
};

將42送到Receiver

Reference

– [What are Senders Good For, Anyway?]What are Senders Good For, Anyway? – Eric Niebler
浅谈The C++ Executors
c++ execution 与 coroutine (一) : CPO与tag_invoke
c++ execution 与 coroutine (二) : execution概述
c++ execution 与 coroutine (三):最简单的receiver与最简单的sender