UVW源码漫谈(二)
2018-06-17 21:55:06来源:未知 阅读 ()
前一篇发布出来之后,我看着阅读量还是挺多的,就是评论和给意见的一个都没有,或许各位看官就跟我一样,看帖子从不回复,只管看就行了。毕竟大家都有公务在身,没太多时间,可以理解。不过没关系,我是不是可以直接想象为我写的东西还挺不错的,KeKe~~。
这一篇介绍一下源代码 ./src/uvw/emitter.hpp 里的东东。由于代码量实在比较大,我就折叠起来了,不然各位看官手指头滚上滚下的,太累了。之后我就写到哪儿贴哪儿的代码,注个大概源代码的位置,有兴趣自己打开源代码对照看看,是不是看的就比较舒服点了。
#pragma once #include <type_traits> #include <functional> #include <algorithm> #include <utility> #include <cstddef> #include <vector> #include <memory> #include <list> #include <uv.h> namespace uvw { /** * @brief The ErrorEvent event. * * Custom wrapper around error constants of `libuv`. */ struct ErrorEvent { template<typename U, typename = std::enable_if_t<std::is_integral<U>::value>> explicit ErrorEvent(U val) noexcept : ec{static_cast<int>(val)} {} /** * @brief Returns the `libuv` error code equivalent to the given platform dependent error code. * * It returns: * * POSIX error codes on Unix (the ones stored in errno). * * Win32 error codes on Windows (those returned by GetLastError() or WSAGetLastError()). * * If `sys` is already a `libuv` error code, it is simply returned. * * @param sys A platform dependent error code. * @return The `libuv` error code equivalent to the given platform dependent error code. */ static int translate(int sys) noexcept { return uv_translate_sys_error(sys); } /** * @brief Returns the error message for the given error code. * * Leaks a few bytes of memory when you call it with an unknown error code. * * @return The error message for the given error code. */ const char * what() const noexcept { return uv_strerror(ec); } /** * @brief Returns the error name for the given error code. * * Leaks a few bytes of memory when you call it with an unknown error code. * * @return The error name for the given error code. */ const char * name() const noexcept { return uv_err_name(ec); } /** * @brief Gets the underlying error code, that is an error constant of `libuv`. * @return The underlying error code. */ int code() const noexcept { return ec; } /** * @brief Checks if the event contains a valid error code. * @return True in case of success, false otherwise. */ explicit operator bool() const noexcept { return ec < 0; } private: const int ec; }; /** * @brief Event emitter base class. * * Almost everything in `uvw` is an event emitter.<br/> * This is the base class from which resources and loops inherit. */ template<typename T> class Emitter { struct BaseHandler { virtual ~BaseHandler() noexcept = default; virtual bool empty() const noexcept = 0; virtual void clear() noexcept = 0; }; template<typename E> struct Handler final: BaseHandler { using Listener = std::function<void(E &, T &)>; using Element = std::pair<bool, Listener>; using ListenerList = std::list<Element>; using Connection = typename ListenerList::iterator; bool empty() const noexcept override { auto pred = [](auto &&element){ return element.first; }; return std::all_of(onceL.cbegin(), onceL.cend(), pred) && std::all_of(onL.cbegin(), onL.cend(), pred); } void clear() noexcept override { if(publishing) { auto func = [](auto &&element){ element.first = true; }; std::for_each(onceL.begin(), onceL.end(), func); std::for_each(onL.begin(), onL.end(), func); } else { onceL.clear(); onL.clear(); } } Connection once(Listener f) { return onceL.emplace(onceL.cend(), false, std::move(f)); } Connection on(Listener f) { return onL.emplace(onL.cend(), false, std::move(f)); } void erase(Connection conn) noexcept { conn->first = true; if(!publishing) { auto pred = [](auto &&element){ return element.first; }; onceL.remove_if(pred); onL.remove_if(pred); } } void publish(E event, T &ref) { ListenerList currentL; onceL.swap(currentL); auto func = [&event, &ref](auto &&element) { return element.first ? void() : element.second(event, ref); }; publishing = true; std::for_each(onL.rbegin(), onL.rend(), func); std::for_each(currentL.rbegin(), currentL.rend(), func); publishing = false; onL.remove_if([](auto &&element){ return element.first; }); } private: bool publishing{false}; ListenerList onceL{}; ListenerList onL{}; }; static std::size_t next_type() noexcept { static std::size_t counter = 0; return counter++; } template<typename> static std::size_t event_type() noexcept { static std::size_t value = next_type(); return value; } template<typename E> Handler<E> & handler() noexcept { std::size_t type = event_type<E>(); if(!(type < handlers.size())) { handlers.resize(type+1); } if(!handlers[type]) { handlers[type] = std::make_unique<Handler<E>>(); } return static_cast<Handler<E>&>(*handlers[type]); } protected: template<typename E> void publish(E event) { handler<E>().publish(std::move(event), *static_cast<T*>(this)); } public: template<typename E> using Listener = typename Handler<E>::Listener; /** * @brief Connection type for a given event type. * * Given an event type `E`, `Connection<E>` is the type of the connection * object returned by the event emitter whenever a listener for the given * type is registered. */ template<typename E> struct Connection: private Handler<E>::Connection { template<typename> friend class Emitter; Connection() = default; Connection(const Connection &) = default; Connection(Connection &&) = default; Connection(typename Handler<E>::Connection conn) : Handler<E>::Connection{std::move(conn)} {} Connection & operator=(const Connection &) = default; Connection & operator=(Connection &&) = default; }; virtual ~Emitter() noexcept { static_assert(std::is_base_of<Emitter<T>, T>::value, "!"); } /** * @brief Registers a long-lived listener with the event emitter. * * This method can be used to register a listener that is meant to be * invoked more than once for the given event type.<br/> * The Connection object returned by the method can be freely discarded. It * can be used later to disconnect the listener, if needed. * * Listener is usually defined as a callable object assignable to a * `std::function<void(const E &, T &)`, where `E` is the type of the event * and `T` is the type of the resource. * * @param f A valid listener to be registered. * @return Connection object to be used later to disconnect the listener. */ template<typename E> Connection<E> on(Listener<E> f) { return handler<E>().on(std::move(f)); } /** * @brief Registers a short-lived listener with the event emitter. * * This method can be used to register a listener that is meant to be * invoked only once for the given event type.<br/> * The Connection object returned by the method can be freely discarded. It * can be used later to disconnect the listener, if needed. * * Listener is usually defined as a callable object assignable to a * `std::function<void(const E &, T &)`, where `E` is the type of the event * and `T` is the type of the resource. * * @param f Avalid listener to be registered. * @return Connection object to be used later to disconnect the listener. */ template<typename E> Connection<E> once(Listener<E> f) { return handler<E>().once(std::move(f)); } /** * @brief Disconnects a listener from the event emitter. * @param conn A valid Connection object */ template<typename E> void erase(Connection<E> conn) noexcept { handler<E>().erase(std::move(conn)); } /** * @brief Disconnects all the listeners for the given event type. */ template<typename E> void clear() noexcept { handler<E>().clear(); } /** * @brief Disconnects all the listeners. */ void clear() noexcept { std::for_each(handlers.begin(), handlers.end(), [](auto &&hdlr){ if(hdlr) { hdlr->clear(); } }); } /** * @brief Checks if there are listeners registered for the specific event. * @return True if there are no listeners registered for the specific event, * false otherwise. */ template<typename E> bool empty() const noexcept { std::size_t type = event_type<E>(); return (!(type < handlers.size()) || !handlers[type] || static_cast<Handler<E>&>(*handlers[type]).empty()); } /** * @brief Checks if there are listeners registered with the event emitter. * @return True if there are no listeners registered with the event emitter, * false otherwise. */ bool empty() const noexcept { return std::all_of(handlers.cbegin(), handlers.cend(), [](auto &&hdlr){ return !hdlr || hdlr->empty(); }); } private: std::vector<std::unique_ptr<BaseHandler>> handlers{}; }; }
一、语言层面的一些好玩的东东
1、(源文件大概 第161行 —— 第185行)
1 static std::size_t next_type() noexcept { 2 static std::size_t counter = 0; 3 return counter++; 4 } 5 6 template<typename> 7 static std::size_t event_type() noexcept { 8 static std::size_t value = next_type(); 9 return value; 10 } 11 12 template<typename E> 13 Handler<E> & handler() noexcept { 14 std::size_t type = event_type<E>(); 15 16 if(!(type < handlers.size())) { 17 handlers.resize(type+1); 18 } 19 20 if(!handlers[type]) { 21 handlers[type] = std::make_unique<Handler<E>>(); 22 } 23 24 return static_cast<Handler<E>&>(*handlers[type]); 25 }
1.1、static
其实说到static,大家看到这里的人应该都不会陌生,比如static函数,static变量等等。也都知道他们的特性,我就不过多说了。
在这里,大家请看第1行——第10行,有一个是静态函数,第二个则是一个函数模板,函数体里分别声明定义了两个静态变量,我们讨论的就是函数模板里的静态变量。看下面的例子:
1 template<typename> 2 static std::size_t event_type() noexcept { 3 static std::size_t value = 0; 4 return value++; 5 } 6 7 int mian(int argc, char* argv[]) 8 { 9 std::cout << event_type<int>() << std::endl; //0 10 std::cout << event_type<int>() << std::endl; //1 11 std::cout << event_type<int>() << std::endl; //2 12 13 std::cout << event_type<double>() << std::endl; //0 14 std::cout << event_type<double>() << std::endl; //1 15 std::cout << event_type<double>() << std::endl; //2 16 }
这里的输出结果我已经在代码里注出来了,对于函数模板中的静态局部变量的初始化规则:
1、函数模板会根据不同的模板参数生成不同的模板函数
2、每一个模板函数中的静态局部变量只被初始化一次
也就是说,对于不同的类型,会生成新的模板函数,函数中会重新定义一个静态变量,并初始化。就相当于两个不同的函数里定义了名字一样的静态局部变量,由于作用域关系,这两个静态局部变量是不相干的。上面的例子,大家切记切记。
1.2、这段代码的意图
了解了前面这段,各位看官跟随我的眼珠子,再来看看第12行——第25行。大体上可以判断,这段代码是要创建并返回一个Handle,这个Handle是怎么创建的呢?
根据模板类型来创建,使用上面提到的模板函数局部静态变量的技巧,可以获得每个模板类型在handles中的索引,如果索引不存在,则创建;如果存在,则返回对应的Handle。如果有看官不理解这其中的奥妙,可以再仔细看看代码揣摩一下(语言表达能力不够,莫怪莫怪)。
而这儿的模板类型实际上就是上一篇说的事件类型,大家从函数名上也可以看出来。这样做最直接的好处就是,不需要用enum为每个事件打上索引,索引是内建的,而事件本身就是一个struct类型,这样不管是调用还是以后扩展都非常灵活。为了方便理解,贴一段上一篇提到的事件注册代码:
1 tcp->on<uvw::ErrorEvent>([](const uvw::ErrorEvent &, uvw::TcpHandle &) { 2 std::cout << "error " << std::endl; 3 });
在此不得不佩服作者的套路之深。吾辈岂有不学之理。
2、std::move() 和 std::forward()
这两个东西真的是非常头疼。当时也看了很多文章,但是要真的自己写却很难表达出意思。不过今天,找到一篇非常好的文章:http://www.cnblogs.com/catch/p/3507883.html
仔细看完,应该就知道为什么会有move和forward,以及它们是在什么时候起到什么样的作用。
3、using
using不就是使用命名空间吗,这有什么好说的。看下面的代码(源文件第96行——第99行):
1 using Listener = std::function<void(E &, T &)>; 2 using Element = std::pair<bool, Listener>; 3 using ListenerList = std::list<Element>; 4 using Connection = typename ListenerList::iterator;
是不是颠覆了一些看官的三观。其实这已经是C++11中就有的语法,功能和typedef差不多,但是typedef不能用于模板,比如上面的第一行。而using是可以的。举个例子:
1 typedef int MT1; //和下面一行等价 2 using MT1 = int; 3 4 template<typename T> 5 typedef std::vector<T> MT3; //错误 6 7 template<typename T> 8 using MT4 = std::vector<T>; //正确
再看刚刚第4行:using Connection = typename ListenerList::iterator; 这里怎么会有一个typename? 以往在我们在写模板时会用到typename 和 class,比如上面的例子,typename是可以换成class的。而这里是用来告诉编译器,ListenerList::iterator 是一个类型。
这里说一些其他的,相信大家很多都在公司上班吧,除非有自由职业编码者?其实在公司里,很多都是历史遗留下来的代码,而且这些代码也经过了时间的验证,经历过很多工程师的优化修改,所以这些代码除非确认出现问题,否则基本不会修改。以后来到公司的,为了适应以前的编码方式和编译器版本,这些语法基本就很难见到了。所以即使有新的标准出来,很多人也不会去关心,去学习。比如公司指定编码环境为WIN7+VS2010,你说2010的编译器怎么可能包含C++11的标准呢?有需求在原来代码上修改,增加就行了。或许这也就导致很多程序员就算是不去学新东西也能凭借经验继续工作。本来C++98就已经包含了C++作为一种面向对象的计算机语言所需要包含的必须的特性了,所谓封装,多态,继承再加上一些标准库,确实对于大多数需要效率的程序已经够用了,这可能也是C++设计的初衷吧。何必再多此一举,学这学那,说到底C++也只是个工具而已,说不定以后就被淘汰了呢!
这种现象也不能说对与错,另外C++17标准也已经出来了,很多人抱怨C++11还没搞明白呢,C++17就来了。知乎百度上对新标准的讨论也是一浪高过一浪,基本可以总结出一点:不管是大牛还是菜鸟,大家还都是对C++很有感情的。所以对于咱们天天用C++的人来说,不用管以后C++发展的怎么样,只要自己用着舒服就行了,但是前提是得先学,学了才有选择的余地,才游刃有余。
说了一堆废话,咱们继续。KeKe~~
4、其他零碎的东东
#pragma once :很明显,在windows编程中,处理头文件相互包含时,就用这个,而在C标准中则是用 #ifndef ... #define ... #endif 。其实C++标准是支持这个东西的,但在Linux环境下很少有人用,大家都比较推崇那种预编译指令。还有一些原因就是有些编译器不支持,大家可以查看 http://zh.cppreference.com/w/cpp/preprocessor/impl 的说明和编译器支持列表,现在大部分编译器都会支持,所以大胆用就是了,如果恰巧不能用那就换过来呗,也不麻烦。
noexcept:指定该函数不抛出异常,比如 int* p = new (std::nothrow) int; 则指定new失败后不抛出异常,而是返回NULL。(这貌似和noexcept不相干,想到什么写什么喽)
override:指定派生类中需要被重写的虚函数。
explicit:阻止类的隐式类型转换。
二、Emitter类
这个类的功能,就是用于事件注册和发送,uvw中很多类都会继承Emitter,用来响应事件。
基本流程就是,调用on()或once()保存注册事件函数,当libuv有回调时,将数据打包为事件结构体,直接调用publish(),在publish()中再调用注册的相应的事件函数。
很多东西都已经在上面说过了,我就仅介绍一下Handle,Handle是定义在Emitter中的内嵌类。几乎实现了Emitter的所有功能,下面上代码:
1 template<typename E> 2 struct Handler final: BaseHandler { 3 using Listener = std::function<void(E &, T &)>; //上面已经说过 4 using Element = std::pair<bool, Listener>; //这里的pair中的bool是用来标记,该Element是否要删除,false为不删除 5 using ListenerList = std::list<Element>; 6 using Connection = typename ListenerList::iterator; 7 8 bool empty() const noexcept override { 9 auto pred = [](auto &&element){ return element.first; }; 10 11 return std::all_of(onceL.cbegin(), onceL.cend(), pred) && 12 std::all_of(onL.cbegin(), onL.cend(), pred); 13 } 14 15 void clear() noexcept override { //清空注册列表,publishing用来标志,是否正在处理事件,如果正在处理,则将Element的first标为true 16 if(publishing) { 17 auto func = [](auto &&element){ element.first = true; }; 18 std::for_each(onceL.begin(), onceL.end(), func); 19 std::for_each(onL.begin(), onL.end(), func); 20 } else { 21 onceL.clear(); 22 onL.clear(); 23 } 24 } 25 26 Connection once(Listener f) { //注册一次性事件 27 return onceL.emplace(onceL.cend(), false, std::move(f)); 28 } 29 30 Connection on(Listener f) { //注册长期事件 31 return onL.emplace(onL.cend(), false, std::move(f)); 32 } 33 34 void erase(Connection conn) noexcept { 35 conn->first = true; 36 37 if(!publishing) { 38 auto pred = [](auto &&element){ return element.first; }; 39 onceL.remove_if(pred); 40 onL.remove_if(pred); 41 } 42 } 43 44 void publish(E event, T &ref) { 45 ListenerList currentL; 46 onceL.swap(currentL); //这里讲onceL和currentL的数据交换,所以onceL在交换之后实际已经没有数据了,从而实现一次性的事件 47 48 auto func = [&event, &ref](auto &&element) { 49 return element.first ? void() : element.second(event, ref); 50 }; 51 52 publishing = true; //标记正在处理事件 53 54 std::for_each(onL.rbegin(), onL.rend(), func); 55 std::for_each(currentL.rbegin(), currentL.rend(), func); 56 57 publishing = false; //标记处理事件结束 58 59 onL.remove_if([](auto &&element){ return element.first; }); //清除onL中first被标记为true的项,与第4行和第15行对应 60 } 61 62 private: 63 bool publishing{false}; 64 ListenerList onceL{}; //保存一次性事件函数列表 65 ListenerList onL{}; //保存长期事件函数列表 66 };
接下来把目光移动到第63行,如果你觉得没什么异常,那就可以跳过了。如果感觉不对劲,也可以很明显看出来是对类成员的初始化操作,但是这种初始化操作不常见,在C++11中,被称作为列表初始化,详细可以参看:http://zh.cppreference.com/w/cpp/language/list_initialization
emitter.hpp文件去除了libuv相关的依赖后,是可以单独拿出来使用的,可以用于单线程回调。但是值得注意的是该类不是线程安全的,另外它也不适用于异步事件,以后我有时间可以把它改造一下,到时候再和各位分享。
三、下一篇
本来说这一篇可能会等个几天才能写出来的,晚上吃了饭就开始写了,已经快12点了,还挺快的。得赶快睡觉了。
下一篇目前暂定说一下UnderlyingType 和 Resource,如果篇幅短的话再加点其他的东西。
这次可能就真得等个几天了,KeKe~~
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- Java固定资产管理系统 源码 jsp ssh 2020-01-09
- Qt事件分发机制源码分析之QApplication对象构建过程 2019-12-08
- STL源码剖析——序列式容器#5 heap 2019-11-07
- stl源码学习(版本2.91)--list 2019-11-05
- webbench网站测压工具源码分析 2019-10-12
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash