1
0
mirror of https://github.com/tearshark/librf.git synced 2024-10-02 00:00:11 +08:00
librf/tutorial/test_async_modern_cb.cpp
2019-04-21 09:05:04 +08:00

247 lines
8.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "librf.h"
#include <chrono>
#include <iostream>
#include <string>
#include <thread>
#include <future>
template<typename _Input_t, typename _Callable_t>
__declspec(noinline)
void tostring_async_originalism(_Input_t&& value, _Callable_t&& callback)
{
std::thread([callback = std::move(callback), value = std::forward<_Input_t>(value)]
{
callback(std::to_string(value));
}).detach();
}
//回调适配器的模板类
//这个默认类以_Callable_t作为真正的回调
//返回无意义的int以便于编译通过
template<typename _Callable_t, typename _Result_t>
struct modern_callback_adapter_t
{
using return_type = int;
using callback_type = _Callable_t;
static std::tuple<callback_type, return_type> traits(_Callable_t&& callback)
{
return { std::forward<_Callable_t>(callback), 0 };
}
};
#define MODERN_CALLBACK_TRAITS(type) \
using _Result_t = type; \
using _Adapter_t = modern_callback_adapter_t<std::decay_t<_Callable_t>, _Result_t>; \
auto adapter = typename _Adapter_t::traits(std::forward<_Callable_t>(callback))
#define MODERN_CALLBACK_CALL() callback = std::move(std::get<0>(adapter))
#define MODERN_CALLBACK_RETURN() return std::move(std::get<1>(adapter))
template<typename _Input_t, typename _Callable_t>
auto tostring_async_macro(_Input_t&& value, _Callable_t&& callback)
{
MODERN_CALLBACK_TRAITS(std::string);
std::thread([MODERN_CALLBACK_CALL(), value = std::forward<_Input_t>(value)]
{
callback(std::to_string(value));
}).detach();
MODERN_CALLBACK_RETURN();
}
//演示如何通过回调适配器模型将异步回调扩展到支持future模式调用链模式以及协程。
//
//一个使用回调处理结果的异步函数,会涉及五个概念:
//_Input_t异步函数的输入参数
//_Callable_t回调函数这个回调必须调用一次且只能调用一次
//_Return_t异步函数的返回值
//_Result_t异步函数完成后的结果值作为回调函数的入参部分这个参数可以有零至多个
//_Exception_t回调函数的异常 如果不喜欢异常的则忽略这个部分,但就得异步代码将异常处置妥当;
//
//在回调适配器模型里_Input_t/_Result_t/_Exception_t(可选)是异步函数提供的功能所固有的部分_Callable_t/_Return_t
//部分并不直接使用而是通过适配器去另外处理。这样给予适配器一次扩展到future模式调用链模式的机会以及支持协程的机会。
//
//tostring_async 演示了在其他线程里将_Input_t的输入值转化为std::string类型的_Result_t。
//然后调用void(std::string &&)类型的_Callable_t。忽视异常处理。
//
template<typename _Input_t, typename _Callable_t>
__declspec(noinline)
auto tostring_async(_Input_t&& value, _Callable_t&& callback)
//-> typename modern_callback_adapter_t<std::decay_t<_Callable_t>, std::string>::return_type
{
using _Result_t = std::string;
//适配器类型
using _Adapter_t = modern_callback_adapter_t<std::decay_t<_Callable_t>, _Result_t>;
//通过适配器获得兼容_Callable_t类型的真正的回调以及返回值_Return_t
auto adapter = typename _Adapter_t::traits(std::forward<_Callable_t>(callback));
//real_callback与callback未必是同一个变量甚至未必是同一个类型
std::thread([real_callback = std::move(std::get<0>(adapter)), value = std::forward<_Input_t>(value)]
{
real_callback(std::to_string(value));
}).detach();
//返回适配器的return_type变量
return std::move(std::get<1>(adapter));
}
template<typename _Input_t>
auto tostring_async_future(_Input_t&& value)
{
std::promise<std::string> _promise;
std::future<std::string> _future = _promise.get_future();
std::thread([_promise = std::move(_promise), value = std::forward<_Input_t>(value)]() mutable
{
_promise.set_value(std::to_string(value));
}).detach();
return std::move(_future);
}
//-------------------------------------------------------------------------------------------------
//下面演示如何扩展tostring_async函数以支持future模式
//一、做一个辅助类
struct use_future_t {};
//二、申明这个辅助类的全局变量。不什么这个变量也行就是每次要写use_future_t{},麻烦些
//以后就使用use_future替代tostring_async的callback参数了。
//这个参数其实不需要实质传参,最后会被编译器优化没了。
//仅仅是要指定_Callable_t的类型为use_future_t
//从而在tostring_async函数内使用偏特化的modern_callback_adapter_t<use_future_t, ...>而已。
inline constexpr use_future_t use_future{};
//将替换use_future_t的真正的回调类。
//此回调类符合tostring_async的_Callable_t函数签名。生成此类的实例作为real_callback交给tostring_async作为异步回调。
//
//future模式下此类持有一个std::promise<_Result_t>,便于设置值和异常
//而将与promise关联的future作为返回值_Return_t让tostring_async返回。
template<typename _Result_t>
struct use_future_callback_t
{
using promise_type = std::promise<_Result_t>;
mutable promise_type _promise;
void operator()(_Result_t&& value) const
{
_promise.set_value(value);
}
void operator()(_Result_t&& value, std::exception_ptr&& eptr) const
{
if (eptr != nullptr)
_promise.set_exception(std::forward<std::exception_ptr>(eptr));
else
_promise.set_value(std::forward<_Result_t>(value));
}
};
//偏特化_Callable_t为use_future_t类型的modern_callback_adapter_t
//真正的回调类型是use_future_callback_t返回类型_Return_t是std::future<_Result_t>。
//配合use_future_callback_t的std::promise<_Result_t>正好组成一对promise/future对。
//promise在真正的回调里设置结果值
//future返回给调用者获取结果值。
template<typename _Result_t>
struct modern_callback_adapter_t<use_future_t, _Result_t>
{
using return_type = std::future<_Result_t>;
using callback_type = use_future_callback_t<_Result_t>;
static std::tuple<callback_type, return_type> traits(const use_future_t&/*没人关心这个变量*/)
{
callback_type real_callback{};
return_type future = real_callback._promise.get_future();
return { std::move(real_callback), std::move(future) };
}
};
//-------------------------------------------------------------------------------------------------
//同理可以制作支持C++20的协程的下列一系列类其实这才是我的最终目的
struct use_awaitable_t {};
inline constexpr use_awaitable_t use_awaitable{};
template<typename _Result_t>
struct use_awaitable_callback_t
{
using promise_type = resumef::promise_t<_Result_t>;
using state_type = typename promise_type::state_type;
resumef::counted_ptr<state_type> _state;
void operator()(_Result_t&& value) const
{
_state->set_value(std::forward<_Result_t>(value));
}
void operator()(_Result_t&& value, std::exception_ptr&& eptr) const
{
if (eptr != nullptr)
_state->set_exception(std::forward<std::exception_ptr>(eptr));
else
_state->set_value(std::forward<_Result_t>(value));
}
};
template<typename _Result_t>
struct modern_callback_adapter_t<use_awaitable_t, _Result_t>
{
using promise_type = resumef::promise_t<_Result_t>;
using return_type = resumef::future_t<_Result_t>;
using callback_type = use_awaitable_callback_t<_Result_t>;
static std::tuple<callback_type, return_type> traits(const use_awaitable_t&)
{
promise_type promise;
return { callback_type{ promise._state }, promise.get_future() };
}
};
//所以我现在的看法是支持异步操作的库尽可能如此设计回调。这样便于支持C++20的协程。以及future::then这样的任务链。
//这才是“摩登C++”!
//使用范例
__declspec(noinline)
void resumable_main_modern_cb()
{
using namespace std::literals;
//使用lambda作为异步回调函数传统用法
//tostring_async_originalism(-1.0, [](std::string && value)
// {
// std::cout << value << std::endl;
// });
tostring_async(1.0, [](std::string && value)
{
std::cout << value << std::endl;
});
/*
std::cout << nocare << std::endl;
std::this_thread::sleep_for(1s);
std::cout << "......" << std::endl;
//支持future的用法
std::future<std::string> f1 = tostring_async_future(5);
std::cout << f1.get() << std::endl;
std::future<std::string> f2 = tostring_async(6.0f, use_future);
std::cout << f2.get() << std::endl;
//支持librf的用法
GO
{
std::string result = co_await tostring_async(10.0, use_awaitable);
std::cout << result << std::endl;
};
resumef::this_scheduler()->run_until_notask();
*/
}