【问题标题】:Boost::ASIO: How can I capture return value from io_service?Boost::ASIO:如何从 io_service 捕获返回值?
【发布时间】:2017-08-11 11:24:45
【问题描述】:

如何从boost::asio::io_service 捕获返回值?是否可以使用一些不涉及重写函数的绑定或任何简单的构造?

以下是一个最小示例。我正在尝试捕获GetSum() 的值返回:

#include <iostream>
#include <boost/asio.hpp>
#include <functional>

using namespace std;

void SayHello()
{
    std::cout<<"Hello!"<<std::endl;
}

template <typename T>
T GetSum(T a, T b)
{
    std::cout<<"Adding " << a << " and " << b << std::endl;
    return a+b;
}

int main(int argc, char *argv[])
{
    boost::asio::io_service ioservice;

    ioservice.post(&SayHello);
    ioservice.post(std::bind(&GetSum<double>,1,2));

    ioservice.run();
    return 0;
}

为什么?因为我正在设计一个线程池,并且我正在考虑我的选项,以使用户能够获得他的函数的返回值,而不必手动将他的函数与另一个函数包装起来,该函数将为他捕获返回值.

我的解决方案:

int main(int argc, char *argv[])
{
    boost::asio::io_service ioservice;

    ioservice.post(&SayHello);
    double sum;
    ioservice.post([&sum]()
    {
        sum = GetSum(1,2);
    });

    ioservice.run();
    std::cout<< sum <<std::endl; //is 3
    return 0;
}

但我仍然希望有一些更简单的绑定或其他东西。

【问题讨论】:

  • 看来你需要某种std::future
  • @teivaz 听起来很对,但我认为期货是明确为std::thread 制作的。它是线程库的一部分。
  • 我认为除了您的解决方案之外,我还会捕获操作完成后在 io_service 中设置的某种 ManualResetEvent (或互斥体 + cv)。这使提交者能够等待操作的结果(而不仅仅是等待所有排队的操作完成 - 就像 ioservice.run() 发生的那样。返回在 ioservice 线程内完成的 std/boost::future 将允许相同的操作。
  • @Matthias247 其实我对 Windows API 并不熟悉,所以我不知道 ManualResetEvent 做了什么。我必须阅读它。无论如何,等待绝对必须按照您提到的方式完成。这些例子只是最小的:)
  • 可能相关:stackoverflow.com/a/22430940/3962537 |有很多类似的问题。不久前,我使用boost::futureboost::promise 实现了我自己的包装器来完成类似的事情。

标签: c++ boost boost-asio stdbind asio


【解决方案1】:

我想出了一个解决方案,灵感来自使用std::future 之类的建议。所以我使用了std::future,并且代码有效。

我所做的只是从io_service 继承,并创建一个新方法post_with_future,它具有返回值的未来。我将不胜感激批评此解决方案以改进它。

#include <iostream>
#include <functional>
#include <type_traits>
#include <future>
#include <boost/asio.hpp>

class future_io_service : public boost::asio::io_service
{
public:
    template <typename FuncType>
    std::future<typename std::result_of<FuncType()>::type> post_with_future(FuncType&& func)
    {
        //keep in mind that std::result_of is std::invoke_result in C++17
        typedef typename std::result_of<FuncType()>::type return_type;
        typedef typename std::packaged_task<return_type()> task_type;
        //since post requires that the functions in it are copy-constructible, we use a shared pointer for the packaged_task since it's only movable and non-copyable
        std::shared_ptr<task_type> task = std::make_shared<task_type>(std::move(func));
        std::future<return_type> returned_future = task->get_future();
        this->post(std::bind(&task_type::operator(),task));
        return returned_future;
    }
};

void SayHello()
{
    std::cout<<"Hello!"<<std::endl;
}

template <typename T>
T GetSum(T a, T b)
{
    std::cout<<"Adding " << a << " and " << b << std::endl;
    return a+b;
}

int main()
{
    future_io_service ioservice;

    ioservice.post(&SayHello);
    auto sum = ioservice.post_with_future(std::bind(&GetSum<int>,1,2));
    ioservice.run();
    std::cout<<sum.get()<<std::endl; //result is 3
    return 0;
}

【讨论】:

  • 不,这很脏。你应该使用async_result 来做这样的事情。看我的回答。
  • 我将尝试使用async_result。但是我可以问一下为什么这很脏吗?
  • 不要对这个词生气 :) 我想说这是一个“黑客”,直到现在我还真的没有看到任何人从 io_service 派生。而您的用例根本不需要。
  • @Arunmu 无意冒犯 :) 我只是不明白原因。
【解决方案2】:

这就是您可以通过使用asio::use_futureasync_result 来做到这一点的方法。请注意,我通过按值传递事物并为总和使用硬编码参数来保持示例简单。

#include <iostream>
#include <thread>
#include <asio.hpp>
#include <asio/use_future.hpp>

int get_sum(int a, int b)
{
  return a + b;
}

template <typename Func, typename CompletionFunction>
auto perform_asyncly(asio::io_service& ios, Func f, CompletionFunction cfun)
{

  using handler_type = typename asio::handler_type
                         <CompletionFunction, void(asio::error_code, int)>::type;

  handler_type handler{cfun};
  asio::async_result<handler_type> result(handler);

  ios.post([handler, f]() mutable {
        handler(asio::error_code{}, f(2, 2));
      });

  return result.get();
}

int main() {
  asio::io_service ios;
  asio::io_service::work wrk{ios};
  std::thread t{[&]{ ios.run(); }};
  auto res = perform_asyncly(ios, get_sum, asio::use_future);
  std::cout << res.get() << std::endl;

  t.join();

  return 0;
}

【讨论】:

  • 不是批评,但您没有遵守我提供的示例中的任何条款。您甚至没有在调用中提供get_sum 的参数!感谢您告诉我有关 use_futureasync_result 的信息。我要去研究他们。
  • 是的,我知道。我真的不想付出那么多我觉得微不足道的努力。我只是想传达一种叫做async_resultuse_future 的东西。
【解决方案3】:

如果目标是有一个简单的类似绑定的函数,它也为您捕获返回值,您可以像这样实现它:

#include <iostream>
#include <boost/asio.hpp>
#include <functional>

using namespace std;

void SayHello()
{
  std::cout<<"Hello!"<<std::endl;
}

template <typename T>
T GetSum(T a, T b)
{
  std::cout<<"Adding " << a << " and " << b << std::endl;
  return a+b;
}

template<typename R, typename F, typename... Args>
auto bind_return_value(R& r, F&& f, Args&&... args)
{
  return [&]()
    {
      r = f(std::forward<Args>(args)...);
    };
}

int main(int argc, char *argv[])
{
  boost::asio::io_service ioservice;

  ioservice.post(&SayHello);
  double sum;
  ioservice.post(bind_return_value(sum, &GetSum<double>, 1, 2));

  ioservice.run();
  std::cout<< sum <<std::endl; //is 3
  return 0;
}

【讨论】:

  • 感谢您的回答。实际上我一直在寻找这样的东西,但作为标准的一部分。让我们拭目以待,看看是否会出现更好的情况:)
  • @TheQuantumPhysicist 当然,这个答案是我只使用标准工具未能做到这一点的结果,但也许有人会在不实现新功能的情况下找到技巧:)
【解决方案4】:

以下解决方案是我计划在自己的应用程序中使用的解决方案。三大特点:

  1. 函数/lambda 是 post_function_use_future() 的参数。要求:函数必须返回 void 以外的值,并且它们必须有零输入参数。注意 SayHello() 现在返回一个 int。

  2. 可以使用任何 Asio 上下文,例如 io_context 和 strands。

  3. 在撰写本文时没有弃用的函数。

在主cpp文件中:

#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include "function_return_type.hpp"

template <typename ExecutionContext, typename FuncWithReturnNoArgs>
auto post_function_use_future(ExecutionContext& ctx, FuncWithReturnNoArgs f)
{
    using handler_type = typename boost::asio::handler_type
        <boost::asio::use_future_t<>, void(boost::system::error_code, return_type_t<FuncWithReturnNoArgs>)>::type;

    using Sig = void(boost::system::error_code, return_type_t<FuncWithReturnNoArgs>);
    using Result = typename boost::asio::async_result<boost::asio::use_future_t<>, Sig>;
    using Handler = typename Result::completion_handler_type;

    Handler handler(std::forward<decltype(boost::asio::use_future)>(boost::asio::use_future));
    Result result(handler);

    boost::asio::post(ctx, [handler, f]() mutable {
        handler(boost::system::error_code(), f());
    });

    return result.get();
}

namespace asio = boost::asio;

int SayHello()
{
    std::cout << "Hello!" << std::endl;
    return 0;
}

template <typename T>
T GetSum(T a, T b)
{
    std::cout << "Adding " << a << " and " << b << std::endl;
    return a + b;
}

int main() {
    asio::io_context io;
    auto wg = asio::make_work_guard(io);

    std::thread t{ [&] { io.run(); } };

    auto res1 = post_function_use_future(io, SayHello);
    res1.get(); // block until return value received.

    auto res2 = post_function_use_future(io, []() {return  GetSum(20, 14); });
    std::cout << res2.get() << std::endl; // block until return value received.

    wg.reset();
    if(t.joinable()) t.join();

    return 0;
}

在 function_return_type.hpp 文件中(非常感谢this solution):

#ifndef FUNCTION_RETURN_TYPE_HPP
#define FUNCTION_RETURN_TYPE_HPP

template <typename F>
struct return_type_impl;

template <typename R, typename... Args>
struct return_type_impl<R(Args...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(Args..., ...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(*)(Args...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(*)(Args..., ...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(&)(Args...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(&)(Args..., ...)> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...)> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...)> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) &> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) &> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) && > { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) && > { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile&&> { using type = R; };

template <typename T, typename = void>
struct return_type
    : return_type_impl<T> {};

template <typename T>
struct return_type<T, decltype(void(&T::operator()))>
    : return_type_impl<decltype(&T::operator())> {};

template <typename T>
using return_type_t = typename return_type<T>::type;

#endif

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    • 2011-06-16
    • 1970-01-01
    • 2017-09-17
    • 1970-01-01
    相关资源
    最近更新 更多