信号分发器在很多游戏项目中都有使用,是一种典型的观察者模式。在游戏编程中,客户端的绘制往往需要靠逻辑数据来驱动,绘制通过监听信号等待逻辑的通知。当然了,逻辑模块跟逻辑模块相互之间的通知也可以使用信号。这种编程模式能够简化模块之间的通信问题,不需要引用烦人的指针调用,导致指针满屏飞的情况。C++恶心的地方就是在大型项目中,指针相当多。
那边,实现一个信号分发器需要哪些功能呢?我们知道,所谓的信号通知,无非就是函数调用。监听一个信号,其本质上就是绑定一个指针,这个问题的难点在于函数指针是各式各样的,那么我们怎么样来管理这些指针呢?毕竟我们是想通过一个类似list的集合来保存这些指针。有的人可能会想到用同一种list保持同一种指针,因为一个信号也许只对应一种函数签名,如果你采用了这种方法,那么很不幸告诉你,你实现起来会困难重重。要是在Java上,这个问题很好解决,因为JAVA上所有的引用类型对象都自动继承自Object对象。在C++上,解决这个问题一有两种方法:1是把所有的指针转换成int数值,这个能解决指针存问题。2是借用函数包装。这两种方法中,方法1显的很粗糙,无法实现类型安全检查;方法2就显示的很优雅,而且能在编译时期进行接口信号是否匹配问题的检查。那么接下来我们的讨论将以方法2为准,来讲解一个实现。
总结一下需要用到的相应的库:一个类型萃取库traits,一个序列化库serialization,一个函数包装库callhelper。如果看过我之前博文的同学,那么就会注意到在下的小心机了,这三个库在下都一一按顺序发布过了。
1. 在调一次信号时只会产生一次序列化和一次反序列化消耗,这个应该是不可避免。大家要是有好的方法欢迎来讨论长知识(我见过会产生一次序列化和n次反序列化的信号库实现,原来公司项目中使用的一次实现就是这样,这个其实是没有必要的)。
2. 这个信号库允许一个信号对应多个不同签名的调用接口,至少实战中到底有没有这个必要,这里暂时不讨论。
3. 支持引用类型及指针类型的传递。
//signalcall2.hpp
#ifndef SIGNALCALL2_INCLUDE
#define SIGNALCALL2_INCLUDE
#include "signalcall_config2.hpp"
#include "signal_type2.hpp"
#include