香橼做空机构网站,广州地铁运营时间,易利购网站怎么做,男女做那事是什 网站上篇文章《静态库和链接选项--whole-archive》提到--whole-archive的一个应用场景#xff1a;C自动注册的工厂#xff0c;这篇文章来填坑。 预备知识 我们经常用工厂类或工厂方法统一管理资源#xff0c;实现资源的创建和使用之间的解耦#xff0c;调用者无需关心资源创建的… 上篇文章《静态库和链接选项--whole-archive》提到--whole-archive的一个应用场景C自动注册的工厂这篇文章来填坑。 预备知识 我们经常用工厂类或工厂方法统一管理资源实现资源的创建和使用之间的解耦调用者无需关心资源创建的细节直接到工厂申请创建好的资源即可。 一般情况下资源提供了统一的接口供使用者调用到工厂的获取也采用统一的方式极大地简化了编码尤其是资源创建比较繁冗的情况下。而且资源统一管理也从某种程度上节省了资源的重复创建带来的开销。 下面是典型的工厂函数的实现 using MsgHandler std::functionbool(std::string_view msg_data);MsgHandler*
create_msg_handler(std::string_view msg_type) {if (msg_type account) {return http_handle;}else if (msg_type pay) {return json_handle;}...else {return nullptr;}
} 这个函数根据不同的消息类型返回对应的消息处理器基本实现了工厂的注册功能。 我们看下这种实现的问题。假设新增一种消息处理器我们需要添加一个if语句然后返回对应的handler看似问题不大。但只要有修改就会容易出错甚至不经意的触碰到键盘的一个键都会引入一个bug这种情况真遇到过所以我们的原则应该是尽量不要动已有的代码要从代码结构的扩展性上下手。 自动注册的工厂模式可以解决这个问题。 自动注册的工厂模式 .
├── msg_handler.cpp
├── msg_handler.h
├── pay_handler.cpp
├── CMakeLists.txt
└── main.cpp 其中msg_handler.[cpp|h]实现工厂pay_handler.cpp提供了pay类型消息的处理器main.cpp演示了消息处理器的使用。 // msg_handler.h
#include functional
#include string_viewusing MsgHandler std::functionbool(std::string_view msg_data);// 注册消息处理器
void
register_msg_handler(const char *msg_type, MsgHandler handler);// 获取指定消息类型的处理器
MsgHandler*
get_msg_handler(const char *msg_type);// msg_handler.cpp
#include map
#include string#include msg_handler.hstatic std::mapstd::string, MsgHandler
get_map() {static std::mapstd::string, MsgHandler map_handlers;return map_handlers;
}void
register_msg_handler(const char *msg_type, MsgHandler handler) {get_map()[msg_type] handler;
}MsgHandler*
get_msg_handler(const char *msg_type) {auto m get_map();auto it m.find(msg_type);if (it ! m.end()) {return it-second;}else {return nullptr;}
} msg_handler实现了简单的工厂调用register_msg_handler注册处理器使用方调用get_msg_handler获取消息处理器。工厂内部使用map存储k为消息类型v为消息处理器。注意map_handlers为函数内部的静态变量之所以这么做主要是给这个静态变量确定的初始化顺序首次调用get_map时初始化保证在注册的时候map_handlers一定是可用的。 下面是pay_handler.cpp文件实现了pay类型的消息处理及自动注册。 #include msg_handler.h
#include stdio.hclass PayHandler {public:PayHandler() { register_msg_handler(pay, PayHandler::handle);}static bool handle(std::string_view msg_data) {printf(pay handle\n);return true;}
};static PayHandler pay_handler; 实现自动注册的逻辑分两步 PayHandler构造函数实现消息处理器的注册声明一个文件作用域的变量初始化时自动调用构造函数从而实现了自动注册。 pay_handler采用全局变量也是可以的这里加了static主要为了避免这个变量作用域过大被滥用。 下面是主流程的实现main.cpp #include msg_handler.h
#include stdio.hint main() {MsgHandler* handle get_msg_handler(pay);if (handle) {(*handle)(test data);}else {printf(not found\n);}return 0;
} 程序正常运行会输出pay handle大家可以先试着编译运行下看看输出结果。 编译运行 如果没有把pay_handler源代码的目标文件链接到可执行文件会输出not found因为工厂里没有找到对应的handler。 大概率编译的指令跟下面的大同小异这里只是简单的使用了-l链接libpayhandler静态库然而链接器最终会优化掉因为通过main.cpp链接器仅能判断出依赖libmsghandler库。 $ g -c msg_handler.cpp
$ g -c pay_handler.cpp
$ ar rcs libmsghandler.a msg_handler.o
$ ar rcs libpayhandler.a pay_handler.o
$ g main.cpp -L. -lpayhandler -lmsghandler -o main
$ ./main
not found 这种情况需要使用--whole-archive选项强制将库链接进可执行文件修改为 g main.cpp -L. \-Wl,--whole-archive -lpayhandler \-Wl,--no-whole-archive \-lmsghandler -o main 在这个例子里--whole-archive是必需的我们无法像上篇文章那样简单的调整一下main.cpp和静态库的位置解决问题貌似也没其他方法了。 附上对应的CMakeLists.txt文件 cmake_minimum_required (VERSION 3.24.0)
project(main)add_library(payhandler STATIC pay_handler.cpp)
add_library(msghandler STATIC msg_handler.cpp)add_executable(${PROJECT_NAME} main.cpp)target_link_libraries(${PROJECT_NAME}msghandler$LINK_LIBRARY:WHOLE_ARCHIVE,payhandler
)