第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定
已解決430363個(gè)問題,去搜搜看,總會(huì)有你想問的

通過c++20協(xié)程制作python生成器

通過c++20協(xié)程制作python生成器

幕布斯7119047 2023-09-12 15:09:02
假設(shè)我有這個(gè) python 代碼:def double_inputs():    while True:        x = yield        yield x * 2gen = double_inputs()next(gen)print(gen.send(1))正如預(yù)期的那樣,它打印“2”。我可以用 c++20 制作一個(gè)生成器,如下所示:#include <coroutine>template <class T>struct generator {    struct promise_type;    using coro_handle = std::coroutine_handle<promise_type>;    struct promise_type {        T current_value;        auto get_return_object() { return generator{coro_handle::from_promise(*this)}; }        auto initial_suspend() { return std::suspend_always{}; }        auto final_suspend() { return std::suspend_always{}; }        void unhandled_exception() { std::terminate(); }        auto yield_value(T value) {            current_value = value;            return std::suspend_always{};        }    };    bool next() { return coro ? (coro.resume(), !coro.done()) : false; }    T value() { return coro.promise().current_value; }    generator(generator const & rhs) = delete;    generator(generator &&rhs)        :coro(rhs.coro)    {        rhs.coro = nullptr;    }    ~generator() {        if (coro)            coro.destroy();    }private:    generator(coro_handle h) : coro(h) {}    coro_handle coro;};generator<char> hello(){    //TODO:send string here via co_await, but HOW???    std::string word = "hello world";    for(auto &ch:word){        co_yield ch;    }}int main(int, char**) {    for (auto i = hello(); i.next(); ) {        std::cout << i.value() << ' ';    }}該生成器只是逐個(gè)字母地生成一個(gè)字符串,但該字符串是硬編碼在其中的。在Python中,不僅可以從生成器中產(chǎn)生一些東西,而且也可以從生成器中產(chǎn)生一些東西。我相信這可以通過 C++ 中的 co_await 來完成。我需要它像這樣工作:generator<char> hello(){    std::string word = co_await producer; // Wait string from producer somehow     for(auto &ch:word){        co_yield ch;    }}int main(int, char**) {    auto gen = hello(); //make consumer    producer("hello world"); //produce string    for (; gen.next(); ) {        std::cout << gen.value() << ' '; //consume string letter by letter    }}我怎樣才能做到這一點(diǎn)?如何使用 c++20 協(xié)程來制作這個(gè)“生產(chǎn)者”?
查看完整描述

1 回答

?
夢(mèng)里花落0921

TA貢獻(xiàn)1772條經(jīng)驗(yàn) 獲得超6個(gè)贊

如果你想這樣做,你基本上有兩個(gè)問題需要克服。


首先,C++ 是一種靜態(tài)類型語言。這意味著在編譯時(shí)需要知道所涉及的所有內(nèi)容的類型。這就是為什么您的generator類型需要是模板,以便用戶可以指定它從協(xié)程到調(diào)用者的引導(dǎo)類型。


因此,如果您想擁有這個(gè)雙向接口,那么您的函數(shù)中的某些內(nèi)容hello必須指定輸出類型和輸入類型。


最簡(jiǎn)單的方法是創(chuàng)建一個(gè)對(duì)象并將const對(duì)該對(duì)象的非引用傳遞給生成器。每次執(zhí)行 a 時(shí)co_yield,調(diào)用者都可以修改引用的對(duì)象,然后請(qǐng)求新值。協(xié)程可以從引用中讀取并查看給定的數(shù)據(jù)。


但是,如果您堅(jiān)持使用協(xié)程的未來類型作為輸出和輸入,那么您需要解決第一個(gè)問題(通過使模板generator采用OutputType和InputType)以及第二個(gè)問題。


看,您的目標(biāo)是為協(xié)程獲取值。問題在于該值的來源(調(diào)用協(xié)程的函數(shù))有一個(gè) future 對(duì)象。但協(xié)程無法訪問future 對(duì)象。它也無法訪問 future 引用的 Promise 對(duì)象。


或者至少,它不能那么容易做到。


有兩種方法可以根據(jù)不同的用例來實(shí)現(xiàn)此目的。第一個(gè)操縱協(xié)程機(jī)制,通過后門進(jìn)入 Promise。第二個(gè)操作 的屬性co_yield來執(zhí)行基本相同的操作。


轉(zhuǎn)換

協(xié)程的 Promise 對(duì)象通常是隱藏的并且無法從協(xié)程訪問。它可以被 Promise 創(chuàng)建的 future 對(duì)象訪問,并充當(dāng) Promise 數(shù)據(jù)的接口。但在機(jī)器的某些部分也可以訪問它c(diǎn)o_await。


具體來說,當(dāng)您對(duì)協(xié)程中的任何表達(dá)式執(zhí)行 a 時(shí)co_await,機(jī)器會(huì)查看您的 Promise 類型以查看它是否具有名為 的函數(shù)await_transform。await_transform如果是這樣,它將在您使用的每個(gè)表達(dá)式上調(diào)用該 Promise 對(duì)象co_await(至少在co_await您直接編寫的表達(dá)式中,而不是隱式等待,例如由 所創(chuàng)建的表達(dá)式co_yield)。


因此,我們需要做兩件事:創(chuàng)建 Promise 類型的重載await_transform,并創(chuàng)建一個(gè)其唯一目的是允許我們調(diào)用該await_transform函數(shù)的類型。


所以看起來像這樣:


struct generator_input {};


...


//Within the promise type:

auto await_transform(generator_input);

快速說明一下。await_transform像這樣使用的缺點(diǎn)是,通過為我們的 Promise 指定該函數(shù)的一個(gè)重載,我們會(huì)影響使用該類型的任何協(xié)程中的每個(gè) co_await重載。co_await對(duì)于生成器協(xié)程來說,這并不是很重要,因?yàn)槌悄M(jìn)行這樣的黑客攻擊,否則沒有太多理由這樣做。但是,如果您正在創(chuàng)建一個(gè)更通用的機(jī)制,可以在其生成過程中明確等待任意可等待項(xiàng),那么您就會(huì)遇到問題。


OK,這樣我們就有了這個(gè)await_transform功能;這個(gè)函數(shù)需要做什么?它需要返回一個(gè)可等待的對(duì)象,因?yàn)閏o_await它將等待它。但這個(gè)可等待對(duì)象的目的是傳遞對(duì)輸入類型的引用。co_await幸運(yùn)的是,用于將可等待轉(zhuǎn)換為值的機(jī)制是由可等待的await_resume方法提供的。所以我們可以只返回一個(gè)InputType&:


//Within the `generator<OutputType, InputType>`:

    struct passthru_value

    {

        InputType &ret_;


        bool await_ready() {return true;}

        void await_suspend(coro_handle) {}

        InputType &await_resume() { return ret_; }

    };



//Within the promise type:

auto await_transform(generator_input)

{

    return passthru_value{input_value}; //Where `input_value` is the `InputType` object stored by the promise.

}

這使協(xié)程可以通過調(diào)用來訪問該值co_await generator_input{};。請(qǐng)注意,這將返回對(duì)該對(duì)象的引用。


該generator類型可以輕松修改,以允許修改InputType存儲(chǔ)在 Promise 中的對(duì)象。只需添加一對(duì)send函數(shù)即可覆蓋輸入值:


void send(const InputType &input)

{

    coro.promise().input_value = input;


void send(InputType &&input)

{

    coro.promise().input_value = std::move(input);

這代表了一種不對(duì)稱的傳輸機(jī)制。協(xié)程在自己選擇的地點(diǎn)和時(shí)間檢索值。因此,它沒有真正的義務(wù)立即響應(yīng)任何變化。這在某些方面是好的,因?yàn)樗试S協(xié)程將自身與有害的更改隔離開來。如果您在容器上使用基于范圍的for循環(huán),則該容器不能被外界直接修改(以大多數(shù)方式),否則您的程序?qū)⒊霈F(xiàn) UB。因此,如果協(xié)程在這種情況下很脆弱,它可以從用戶那里復(fù)制數(shù)據(jù),從而阻止用戶修改它。


總而言之,所需的代碼并沒有那么大。下面是經(jīng)過這些修改的代碼的可運(yùn)行示例:


#include <coroutine>

#include <exception>

#include <string>

#include <iostream>


struct generator_input {};



template <typename OutputType, typename InputType>

struct generator {

    struct promise_type;

    using coro_handle = std::coroutine_handle<promise_type>;


    struct passthru_value

    {

        InputType &ret_;


        bool await_ready() {return true;}

        void await_suspend(coro_handle) {}

        InputType &await_resume() { return ret_; }

    };


    struct promise_type {

        OutputType current_value;

        InputType input_value;



        auto get_return_object() { return generator{coro_handle::from_promise(*this)}; }

        auto initial_suspend() { return std::suspend_always{}; }

        auto final_suspend() { return std::suspend_always{}; }

        void unhandled_exception() { std::terminate(); }

        auto yield_value(OutputType value) {

            current_value = value;

            return std::suspend_always{};

        }


        void return_void() {}


        auto await_transform(generator_input)

        {

            return passthru_value{input_value};

        }

    };


    bool next() { return coro ? (coro.resume(), !coro.done()) : false; }

    OutputType value() { return coro.promise().current_value; }


    void send(const InputType &input)

    {

        coro.promise().input_value = input;

    } 


    void send(InputType &&input)

    {

        coro.promise().input_value = std::move(input);

    } 


    generator(generator const & rhs) = delete;

    generator(generator &&rhs)

        :coro(rhs.coro)

    {

        rhs.coro = nullptr;

    }

    ~generator() {

        if (coro)

            coro.destroy();

    }

private:

    generator(coro_handle h) : coro(h) {}

    coro_handle coro;

};


generator<char, std::string> hello(){

    auto word = co_await generator_input{};


    for(auto &ch: word){

        co_yield ch;

    }

}


int main(int, char**)

{

    auto test = hello();

    test.send("hello world");


    while(test.next())

    {

        std::cout << test.value() << ' ';

    }

}

變得更有收獲

使用顯式的替代方法co_await是利用 的屬性co_yield。即,co_yield是一個(gè)表達(dá)式,因此它有一個(gè)值。具體來說,它(大部分)相當(dāng)于co_await p.yield_value(e)Promisep對(duì)象(哦?。?,并且e是我們要產(chǎn)生的對(duì)象。


幸運(yùn)的是,我們已經(jīng)有了一個(gè)yield_value函數(shù);它返回std::suspend_always。但它也可以返回一個(gè)始終掛起的對(duì)象,但也可以將其co_await解包為InputType&:


struct yield_thru

{

    InputType &ret_;


    bool await_ready() {return false;}

    void await_suspend(coro_handle) {}

    InputType &await_resume() { return ret_; }

};


...


//in the promise

auto yield_value(OutputType value) {

    current_value = value;

    return yield_thru{input_value};

}

這是一種對(duì)稱傳輸機(jī)制;對(duì)于您產(chǎn)生的每一個(gè)值,您都會(huì)收到一個(gè)值(可能與以前的值相同)。與顯式方法不同,在開始生成它們之前co_await您無法接收值。這對(duì)于某些接口可能很有用。


當(dāng)然,您可以根據(jù)需要將它們組合起來。


查看完整回答
反對(duì) 回復(fù) 2023-09-12
  • 1 回答
  • 0 關(guān)注
  • 152 瀏覽
慕課專欄
更多

添加回答

舉報(bào)

0/150
提交
取消
微信客服

購課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)