c++ 如何检查有效的boost asio CompletionToken?

jckbn6z7  于 2022-11-20  发布在  其他
关注(0)|答案(1)|浏览(260)

我正在写Boost。Asio风格的异步函数,它接受CompletionToken参数。参数可以是函数,函数对象,lambda表达式,future,waitable等等。
CompletionToken是模板参数。如果我不限制参数,函数可能匹配意外参数,如int。所以我想用std::enable_if写一些限制。(我的环境是C++17)。
如果CompletionToken带参数,那么我可以使用std::is_invocable进行检查。
请参见示例代码中的f1
但是,我遇到了一个问题,如果CompletionToken不带参数,那么boost::asio::use_future就会出错。
请参见示例代码中的f2
经过一些尝试和错误,我得到了我的解决方案。这是连接std::is_invocableuse_future_t检查或(||)。
请参见示例代码中的f3
另外,我不确定Boost.Asio支持的其他特性(例如use_awaitable_t)是否需要类似的直接匹配检查。
我试着找到Boost。Asio提供类型特征或 predicate ,如is_completion_token,但我找不到。
有没有更好的方法来检查CompletionToken
Godbolt链接https://godbolt.org/z/sPeMo1GEK
完整代码:

#include <type_traits>
#include <boost/asio.hpp>

// Callable T takes one argument
template <
    typename T,
    std::enable_if_t<std::is_invocable_v<T, int>>* = nullptr
>
void f1(T) {
}

// Callable T takes no argument
template <
    typename T,
    std::enable_if_t<std::is_invocable_v<T>>* = nullptr
>
void f2(T) {
}

template <template <typename...> typename, typename>
struct is_instance_of : std::false_type {};

template <template <typename...> typename T, typename U>
struct is_instance_of<T, T<U>> : std::true_type {};

// Callable T takes no argument
template <
    typename T,
    std::enable_if_t<
        std::is_invocable_v<T> ||
        is_instance_of<boost::asio::use_future_t, T>::value
    >* = nullptr
>
void f3(T) {
}

int main() {
    // no error
    f1([](int){});
    f1(boost::asio::use_future);

    // same rule as f1 but use_future got compile error
    f2([](){});
    f2(boost::asio::use_future); // error

    // a little complecated typechecking, then no error
    f3([](){});
    f3(boost::asio::use_future);
}

输出:

Output of x86-64 clang 13.0.1 (Compiler #1)
<source>:45:5: error: no matching function for call to 'f2'
    f2(boost::asio::use_future); // error
    ^~
<source>:17:6: note: candidate template ignored: requirement 'std::is_invocable_v<boost::asio::use_future_t<std::allocator<void>>>' was not satisfied [with T = boost::asio::use_future_t<>]
void f2(T) {
     ^
1 error generated.
toe95027

toe950271#

如果你有c++20的概念,请看下面。否则,请继续阅读。
当您希望使用Asio正确实现异步结果协议时,可以使用async_result特征,或者这里介绍的async_initiate
这应该是SFINAE的可靠密钥。async_result的模板参数包括标记 * 和 * 完成签名:
Live On Compiler Explorer

#include <boost/asio.hpp>
#include <iostream>
using boost::asio::async_result;

template <typename Token,
          typename R = typename async_result<std::decay_t<Token>, void(int)>::return_type>
void f1(Token&&) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
}

template <typename Token,
          typename R = typename async_result<std::decay_t<Token>, void()>::return_type>
void f2(Token&&) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
}

int main() {
    auto cb1 = [](int) {};
    f1(cb1);
    f1(boost::asio::use_future);
    f1(boost::asio::use_awaitable);
    f1(boost::asio::detached);
    f1(boost::asio::as_tuple(boost::asio::use_awaitable));

    auto cb2 = []() {};
    f2(cb2);
    f2(boost::asio::use_future);
    f2(boost::asio::use_awaitable);
    f2(boost::asio::detached);
    f2(boost::asio::as_tuple(boost::asio::use_awaitable));
}

已打印

void f1(Token&&) [with Token = main()::<lambda(int)>&; R = void]
void f1(Token&&) [with Token = const boost::asio::use_future_t<>&; R = std::future<int>]
void f1(Token&&) [with Token = const boost::asio::use_awaitable_t<>&; R = boost::asio::awaitable<int, boost::asio::any_io_executor>]
void f1(Token&&) [with Token = const boost::asio::detached_t&; R = void]
void f1(Token&&) [with Token = boost::asio::as_tuple_t<boost::asio::use_awaitable_t<> >; R = boost::asio::awaitable<std::tuple<int>, boost::asio::any_io_executor>]
void f2(Token&&) [with Token = main()::<lambda()>&; R = void]
void f2(Token&&) [with Token = const boost::asio::use_future_t<>&; R = std::future<void>]
void f2(Token&&) [with Token = const boost::asio::use_awaitable_t<>&; R = boost::asio::awaitable<void, boost::asio::any_io_executor>]
void f2(Token&&) [with Token = const boost::asio::detached_t&; R = void]
void f2(Token&&) [with Token = boost::asio::as_tuple_t<boost::asio::use_awaitable_t<> >; R = boost::asio::awaitable<std::tuple<>, boost::asio::any_io_executor>]

C20概念
现在请记住,由于部分模板示例化,上面的代码“太松散”了。async_result的一些部分实际上没有被使用。这意味着f2(cb1);will actually compile
链接的文档甚至包括C
20 completion_token_for<Sig>概念,让您可以毫不费力地做到精确:******

template <boost::asio::completion_token_for<void(int)> Token> void f1(Token&&) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
}

template <boost::asio::completion_token_for<void()> Token> void f2(Token&&) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
}

否则

在实践中,您将始终遵循Asio配方,这保证了所有部件都被使用。
示例:

template <typename Token>
typename asio::async_result<std::decay_t<Token>, void(error_code, int)>::return_type
async_f1(Token&& token) {
    auto init = [](auto completion) {
        auto timer =
            std::make_shared<asio::steady_timer>(boost::asio::system_executor{}, 1s);
        std::thread(
            [timer](auto completion) {
                error_code ec;
                timer->wait(ec);
                std::move(completion)(ec, 42);
            },
            std::move(completion))
            .detach();
    };

    return asio::async_result<std::decay_t<Token>, void(error_code, int)>::initiate(
        init, std::forward<Token>(token));
}

相关问题