效果处理程序允许使用用户定义的计算效果进行编程,应用程序包括自定义轻量级并发(线程、异步等待、参与者、生成器)、错误处理、依赖项注入等。效果处理程序起源于函数式编程领域,这个高度实验性的库的主要目标是探索它们如何适合于面向对象的C++设置。
该库依赖于现代C++特性(移动语义、可变模板、编译时评估),以实现优雅的程序员级接口、处理程序的内存管理和相对类型安全性。在内部,它使用boost::context库进行调用堆栈操作,因此它只实现了一次性处理程序。
参考资料——图书馆的详细说明';s API和关于库的总体设计的简短讨论。对于已经熟悉效果处理程序的读者来说,这是一个合适的介绍(例如,通常在函数式编程的上下文中讨论效果处理程序)。
即将到来:教程-介绍使用效果处理程序编程。适合不熟悉处理程序的读者。
作为一个预览,我们可以使用效果处理程序为协作轻量级线程定义我们自己的小库。程序员';s接口将包括两个函数,yield和fork,以及一个实现调度器的类:
空隙率();//线程用来放弃控制void fork(std::function<;void()>;proc);//启动一个新的线程类调度程序{public:static void Start(std::function<;void()>;f);};
静态成员函数Start以f作为第一个线程的主体启动调度程序。当所有线程完成任务时,它返回。
为了实现这个接口,我们首先定义两个命令,它们是用于将控制从客户机代码传输到处理程序的数据结构。我们实现了yield和fork来调用这些命令。(OneShot类的名称应该是提醒程序员,我们只处理单次处理程序,这意味着不能两次恢复相同的恢复)。
#包括";cpp效应/cpp效应。h";使用名称空间效果;结构屈服:命令<;void>;{ }; struct Fork:Command<;void>;{std::function<;void()>;proc;};void yield(){OneShot::InvokeCmd(yield{});}void fork(std::function<;void()>;proc){OneShot::InvokeCmd(Fork{},proc});}
我们定义了调度器,它是一个处理程序,可以通过将恢复(即捕获的继续)推送到队列来解释这两个命令。
//Res是使用Res=std::unique_ptr<;恢复<;void,void>>;;类调度程序:公共处理程序<;无效,无效,屈服,分叉>;{public:static void Start(std::function<;void()>;f){Run(f);while(!queue.empty()){//循环调度自动恢复=std::move(queue.front());queue.pop_front();OneShot::Resume(std::move(Resume));}私有:静态标准::列表<;Res>;队列静态void运行(std::function<;void()>;){OneShot::Handle<;Scheduler>;(f);}void CommandClause(Yield,Res r)重写{queue.push_back(std::move(r));}void CommandClause(Fork f,Res r)重写{queue.push_back(std::move(r));queue.push_back(OneShot::makerestoration<;void>;(std::bind(Run,f.proc));}void ReturnClause()覆盖{};标准:列表<;Res>;调度程序::队列;
那';这就是一切!现在,我们可以通过启动几个线程来测试我们的库:
void worker(intk){for(inti=0;i<;10;++i){std::cout<;<;k;yield();}void starter(){for(int i=0;i<;5;++i){fork(std::bind(worker,i));}int main(){Scheduler::Start(starter);//Output://0102103210432104321043210432104321043210432104432434}
[1] -在库中,处理程序是对象,因此它们可以自然地包含任何数据、辅助函数和额外的程序员';s接口。
编译库和示例的最简单方法是使用cmake。在任何非古代版本中都需要cmake和boost。例如,以下内容可以在macOS上实现:
这将构建库和示例。您可以通过运行一个示例来检查它是否有效。下面将运行线程示例-您可以在输出中看到线程的交错: