Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how can a c++ concept combine concepts?

I have inherited the following:

template <typename T>
concept IsAwaiter = requires {
  typename T::await_ready;
  typename T::await_suspend;
  typename T::await_resume;
};

template <typename ...AWAITABLES>
concept IsAwaitables = typename std::conjunction<IsAwaiter<AWAITABLES>...>::type;

Building this with clang 10.0.0 results in the following error:

IsAwaiter.h:43:50: error: template argument for template type parameter must be a type

Perhaps just a simple syntax issue, but I've found it hard to find an example which shows how to create a concept based on a variadic template concept parameter.

Any help appreciated!

like image 300
Bob Pretzker Avatar asked Sep 08 '20 18:09

Bob Pretzker


2 Answers

std::conjunction is for type traits. std::conjunction<IsAwaiter<AWAITABLES>...> is a type with a member static bool value = (IsAwaiter<AWAITABLES>::value && ...), where each IsAwaiter<AWAITABLES> is itself expected to be a type trait with its own static bool member value. This is nonsensical when IsAwaiter<AWAITABLES> is a concept, because concepts are not type traits. They are "just" booleans. Use a fold expression.

template <typename... AWAITABLES>
concept IsAwaitables = (IsAwaiter<AWAITABLES> && ...);

That's it.

struct Dummy {
    using await_ready = Dummy;
    using await_suspend = Dummy;
    using await_resume = Dummy;
};

int main() {
    static_assert(IsAwaitables<>);
    static_assert(IsAwaitables<Dummy>);
    static_assert(IsAwaitables<Dummy, Dummy>);
}
like image 130
HTNW Avatar answered Nov 11 '22 08:11

HTNW


As HTNW points out, you want:

template <typename ...T>
concept IsAwaitables =  (IsAwaiter<T> && ...);

But really, do you even need this concept at all? You can just use IsAwaiter directly. And it probably should just be named Awaiter - the typical convention for concepts is to name them as nouns rather than questions (e.g. Range vs IsRange).

If you're taking a parameter pack, you would want to use it anyway:

template <Awaiter... T>
void f(T... awaiters);

and the same with the abbreviated function template syntax:

void f(Awaiter auto... awaiters);

or if you have a fixed number of them, it especially doesn't make sense:

template <Awaiter T, Awaiter U>
void f(T, U);

And even in other contexts where it doesn't neatly fit, it seems better to just manually use Awaiter with a fold-expression. So I question the need for the conjunction concept to begin with.

like image 33
Barry Avatar answered Nov 11 '22 08:11

Barry