C++ Logo

std-proposals

Advanced search

回复: Delay the judgement for coroutine function after the instantiation of template entity.

From: chuanqi.xcq <yedeng.yd_at_[hidden]>
Date: Thu, 21 Jan 2021 10:43:51 +0800
Hi, all,

I am not sure if this reply could post to all the people who had reply to the original email. I am not familiar with this mailing system.

> I brought this up on the cpplang Slack (in the #coroutines channel). Personally I tend to agree with your take, but the consensus in Slack seemed to be "You shouldn't want that."

May I ask why they think it is better not to do that?

> I'm interested to see your Clang patch. (How short is it?) It might be interesting to see if you could rework it into a conforming extension (by adding a diagnostic "ISO C++ forbids coroutines to contain `return` statements, even in discarded statements") and post it for review.

Without testcases, the length of the clang patch is about 100 lines. I made it in clang-8.x. Do you think it is better to post the patch by the mailing system directly or refactor it to the main branch and post it to phabraicator?

>> To solve your actual problem, in C++20, consider using plain old macros, e.g.
>
>> This still lets you choose between USE_CORO=0 and USE_CORO=1 at compile time. The downside is that (at least with this naive approach) it lets you choose only one of these per compilation. If you want to have both the coro and non-coro versions living side by side in the same program, you'd have to put this in a header file and do, like,

Yes, we think about macro solution firstly. And giveup it since we it need a lot of re-compilation. A macro in each header solution seems to imply a macro pollution problem. In fact, we use a curious CodeGen technique to solve our problem, which seems like another preprocess stage. But I think this may be too far from our intuition.

> What if we specialized a function template templated on bool, to have one version that's a coroutine, and one version that's a normal function?

This would acquire user to write whole code twice, which is not wanted.

>> I'm curious to see a complex example too. Especially since the main
point of a coroutine is its ability to `co_await` on other processes,
and that's not something that's very easy to `if constexpr` your way
around. Especially since the variables created in an `if constexpr`
block are local to that block, so you can't exactly do this:
>
>> ```
>>if constexpr(UseCoro)
>>{
>> auto value = co_await(coro_expr);
>>}
>>else
>>{
>> auto value = regular_expr
>>}
>>
>>//Use `value`
>>```

Yes, my personal solution based on constexpr-if support for coroutine is:
```
#define MAYCOAWAITEXPR(UseCoro, GetMethod, ARGS...) \
    ({ \
        decltype(GetMethod<false>(ARGS...)) value; \
        if constexpr (UseCoro) \
            value = co_await GetMethod<UseCoro>(ARGS...); \
       else \
            value = GetMethod<UseCoro>(ARGS...); \
        value; \
    })
auto value = MAYCOAWAITEXPR(UseCoro, GetMethod);
```
The macro MAYCOAWAITEXPR uses GNU statement expression extension.

> I do agree that it would be useful if Chuanqi provided a more fleshed-out and realistic example.
> I'm curious to see a complex example too.

Let me try to give a precise and short example. In our situation, the original codes consists of normal function and chains of calls. The length of the longest chain of calls could be nearly over 40.

After stackless coroutine is knwon to be in C++20 in 2019, we try to use coroutine to refactor our codes extensively, which means a lot of effort.

The method of our refactoring is change a normal function to coroutine function, whcih would change the return type from int to task<int> and change a normal function call to a co_await expression.

Note that we don't change every function to coroutine function and changed every function call to co_await expression. Maybe this is a silly note.

And after we refactor these codes, we find that coroutine give a great perform improvement when the concurrency is very very high. But when the concurrency is not so high, the performance is poor.

> I'm also curious to know what situations they find coroutines
performing poorly for.

From our analysis, the reason why coroutines performing poorly is the big try-catch statement inserted by the coroutine standard. It is imported by the design and compiler couldn't do much about that. And we also know it is much much harder to change the exception specification.

> I'm curious to see a complex example too.

I think it is hard to give an concise example in codes, let me try it.

Before refactoring, the codes seems like:
```
int funcA(....) {
    // some logics
    int v = funcB(...);
    // other logics...
}

int funcB(....) {
    // some logics
    int v = funcC(...);
    // other logics...
}
int funcC(...) {
    // an actual asynchoronous situation
    ...
}
```

After refacoring:

```
task<int> funcA(....) {
    // some logics
    int v = co_awiat funcB(...);
    // other logics...
}

task<int> funcB(....) {
    // some logics
    int v = co_await funcC(...);
    // other logics...
}
task<int> funcC(...) {
    // an actual asynchoronous situation

    co_await something;
     ...
    co_return something;

}
```
And we want to:

```

template<bool UseCoro>
CondCoro<UseCoro, int> funcA(....) {
    // some logics
    int v =MAYCOAWAITEXPR(UseCoro, funcB, ...);
    // other logics...
}
task<int> funcB(....) {
    // some logics
    int v = MAYCOAWAITEXPR(UseCoro, funcC, ...);
    // other logics...
}
task<int> funcC(...) {
    // an actual asynchoronous situation

    co_await something;
     ...
    co_return something;

}
```

> I brought this up on the cpplang Slack (in the #coroutines channel). Personally I tend to agree with your take, but the consensus in Slack seemed to be "You shouldn't want that."

Here my point is that it is natural to make constexpr-if work for coroutines. And this suggestion looks really harmless. So I am curious about the reason why people don't want to make constexpr-if work for coroutine.

Although we had used curious technique to solve our real problem, I think it is benefit and harmless for C++ to enable this feature.

Thanks,
Chuanqi
------------------------------------------------------------------
发件人:Jason McKesson via Std-Proposals <std-proposals_at_[hidden]>
发送时间:2021年1月21日(星期四) 01:42
收件人:std-proposals <std-proposals_at_lists.isocpp.org>
抄 送:Jason McKesson <jmckesson_at_gmail.com>
主 题:Re: [std-proposals] Delay the judgement for coroutine function after the instantiation of template entity.

On Wed, Jan 20, 2021 at 11:12 AM Arthur O'Dwyer via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> On Wed, Jan 20, 2021 at 9:21 AM Omry Noam via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>
>> What if we specialized a function template templated on bool, to have one version that's a coroutine, and one version that's a normal function?
>> Maybe something like: https://godbolt.org/z/7TEnb6
>> (Code adapted from cppreference as an example)
>
>
> I believe that's well-formed, but I'm pretty sure OP's main goal is to avoid writing the code twice.
>
> I do agree that it would be useful if Chuanqi provided a more fleshed-out and realistic example. When the body of the function is only one line, it's easy to say "just write it twice." If the function were more complicated, it would be more obvious why "write everything twice" wouldn't be an acceptable solution.
> But I personally can't come up with such an example off the top of my head; I'm not good enough at coroutine stuff.

I'm curious to see a complex example too. Especially since the main
point of a coroutine is its ability to `co_await` on other processes,
and that's not something that's very easy to `if constexpr` your way
around. Especially since the variables created in an `if constexpr`
block are local to that block, so you can't exactly do this:

```
if constexpr(UseCoro)
{
  auto value = co_await(coro_expr);
}
else
{
  auto value = regular_expr
}

//Use `value`
```

You'd have to use the `value` within those blocks. So whatever use of
the result that exists would necessarily need to be duplicated. Or
copied out into some shared variable or something.

I'm also curious to know what situations they find coroutines
performing poorly for.
-- 
Std-Proposals mailing list
Std-Proposals_at_[hidden]
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2021-01-20 20:44:01