Skip to content

Instantly share code, notes, and snippets.

@sayurin
Last active July 12, 2016 05:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sayurin/7232bdc946c5c3794df880ffd4057e39 to your computer and use it in GitHub Desktop.
Save sayurin/7232bdc946c5c3794df880ffd4057e39 to your computer and use it in GitHub Desktop.
#pragma once
#include <future>
#ifdef USE_AWAIT
#include <experimental/resumable>
#include <boost/asio/use_future.hpp>
#else
#include <exception>
#include <memory>
#include <type_traits>
#include <utility>
#include <boost/asio/spawn.hpp>
#define co_await
#endif
namespace coroutine {
namespace detail {
#ifdef USE_AWAIT
using handler = boost::asio::use_future_t<>;
template<class Result> using result = std::future<Result>;
template<class Context, class... Args>
inline auto spawn(Context&&, Args&&... args) -> decltype(std::invoke(std::forward<Args>(args)..., boost::asio::use_future)) {
return std::invoke(std::forward<Args>(args)..., boost::asio::use_future);
}
#else
using handler = boost::asio::yield_context;
template<class Result> using result = Result;
template<class Tuple, std::size_t... Index>
inline void invoke_and_set(Tuple&& tuple, boost::asio::yield_context& handler, std::promise<void>& promise, std::index_sequence<Index...>) {
std::invoke(std::get<Index>(std::forward<Tuple>(tuple))..., handler);
promise.set_value();
}
template<class Tuple, class Result, std::size_t... Index>
inline void invoke_and_set(Tuple&& tuple, boost::asio::yield_context& handler, std::promise<Result>& promise, std::index_sequence<Index...>) {
auto result = std::invoke(std::get<Index>(std::forward<Tuple>(tuple))..., handler);
promise.set_value(result);
}
template<class Context, class... Args>
inline auto spawn(Context&& context, Args&&... args) {
auto promise = std::make_unique<std::promise<decltype(std::invoke(args..., std::declval<boost::asio::yield_context>()))>>();
auto future = promise->get_future();
boost::asio::spawn(std::forward<Context>(context), [tuple = std::forward_as_tuple(args...), promise = std::move(promise)](auto handler) {
try {
detail::invoke_and_set(std::forward<std::tuple<Args...>>(tuple), handler, *promise, std::index_sequence_for<Args...>{});
}
catch (...) {
promise->set_exception(std::current_exception());
}
});
return future;
}
#endif
}
// coroutine::invoke()が呼び出せるコルーチンの引数型
using handler = detail::handler;
// coroutine::invoke()が呼び出せるコルーチンの戻り値型
template<class Result> using result = detail::result<Result>;
/// <summary>コルーチンを呼び出します。</summary>
/// <param name="context">boost::asio::spawn()の第1引数となるため、boost::asio::io_service&amp; / boost::asio::io_service::strandなど</param>
/// <param name="callable">呼び出すコルーチン、引数はargs...の後にcoroutine::handler、戻り値はcoroutine::result&lt;Result&gt;</param>
/// <param name="args">呼び出すコルーチンの引数</param>
/// <returns>std::future&lt;Result&gt;になります。</returns>
template<class Context, class Callable, class... Args>
inline decltype(auto) spawn(Context&& context, Callable&& callable, Args&&... args) {
return detail::spawn(std::forward<Context>(context), std::forward<Callable>(callable), std::forward<Args>(args)...);
}
}
#define _WIN32_WINDOWS _WIN32_WINDOWS_WIN7
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define BOOST_DATE_TIME_NO_LIB
#define BOOST_ERROR_CODE_HEADER_ONLY
#define BOOST_REGEX_NO_LIB
#define BOOST_SYSTEM_NO_LIB
#include <iostream>
#include <thread>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
using boost::asio::io_service;
using boost::asio::ip::tcp;
#include "coroutine.h"
coroutine::result<int> run(io_service& ios, coroutine::handler handler) {
tcp::resolver resolver{ ios };
auto itor = co_await resolver.async_resolve(tcp::resolver::query{ "www.google.com", "http" }, handler);
int count = 0;
for (tcp::resolver::iterator end; itor != end; ++itor, ++count)
std::cout << itor->endpoint() << std::endl;
return count;
}
int main() {
io_service ios;
io_service::work work{ ios };
std::thread thread{ [&] { ios.run(); } };
auto result = coroutine::spawn(ios, run, ios);
std::cout << "spawned." << std::endl;
auto count = result.get();
std::cout << "completed, result count =" << count << std::endl;
ios.stop();
thread.join();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment