C++自动注册的工厂与--whole-archive

这篇具有很好参考价值的文章主要介绍了C++自动注册的工厂与--whole-archive。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上篇文章《静态库和链接选项--whole-archive》提到--whole-archive的一个应用场景:C++自动注册的工厂,这篇文章来填坑。

预备知识

我们经常用工厂类或工厂方法统一管理资源,实现资源的创建和使用之间的解耦,调用者无需关心资源创建的细节,直接到工厂申请创建好的资源即可。

一般情况下,资源提供了统一的接口供使用者调用,到工厂的获取也采用统一的方式,极大地简化了编码,尤其是资源创建比较繁冗的情况下。而且,资源统一管理也从某种程度上节省了资源的重复创建带来的开销。

下面是典型的工厂函数的实现:

using MsgHandler = 
    std::function<bool(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_view>

using MsgHandler = 
    std::function<bool(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.h"

static std::map<std::string, MsgHandler>& 
get_map() {
  static std::map<std::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.h>

class 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;

实现自动注册的逻辑分两步:

  1. PayHandler构造函数实现消息处理器的注册;

  2. 声明一个文件作用域的变量,初始化时自动调用构造函数,从而实现了自动注册。

pay_handler采用全局变量也是可以的,这里加了static主要为了避免这个变量作用域过大被滥用。

下面是主流程的实现:main.cpp

#include "msg_handler.h"
#include <stdio.h>

int 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文件:文章来源地址https://www.toymoban.com/news/detail-772601.html

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>"
)

到了这里,关于C++自动注册的工厂与--whole-archive的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++设计模式(工厂模式)

    本篇文章正式带大家来学习C++中的设计模式,这篇文章主要带大家学习工厂模式。 工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的接口,但将具体对象的实例化延迟到子类或具体工厂类中。工厂模式通过解耦客户端代码和创建具体对象的过程,使客

    2024年02月11日
    浏览(28)
  • 【13】c++设计模式——>简单工厂模式

    c++中的工厂模式是一种创建型设计模式,它提供一种创建对象的接口,但具体创建的对象类型可以在运行时决定,这样可以将对象的创建与使用代码分离,提高代码的灵活性和可维护性。 在c++中实现工厂模式,通常会定义一个工厂类,该类负责创建对象。工厂类通常具有一个

    2024年02月07日
    浏览(26)
  • 工业平板电脑实现工厂自动化设备无需手动连接

    随着中国经济的快速发展和材料水平的不断提高,制造业的竞争日益激烈,市场竞静力逐渐转向质量、效率和价格服务,制造业企业面临更大的挑战,数据转型迫在眉睫。对工业平板电脑的需求也在增加,面向行业的工业平板电脑已成为新的趋势。 工业平板电脑在智能工厂中

    2023年04月25日
    浏览(52)
  • C++设计模式创建型之工厂模式整理

    一、工厂模式分类         工厂模式属于创建型模式,一般可以细分为简单工厂模式、工厂模式和抽象工厂模式。每种都有不同的特色和应用场景。 二、工厂模式详情 1、简单工厂模式 1)概述         简单工厂模式相对来说,在四人组写的《设计模式------可复用面向对

    2024年02月14日
    浏览(26)
  • C++ 程序设计:四大模式(工厂+装饰+策略+观察者)

    \\\"工厂+装饰+策略+观察者\\\"是常见且常用的设计模式之一,但并不是指称\\\"四大模式\\\"的官方术语。 \\\" 四大模式 \\\"通常是指指令式面向对象编程中的四个基本概念: 封装 、 继承 、 多态 和 抽象 。这四个概念是面向对象编程的基石。 工厂模式( 例:工厂方法模式 )中,通常存在

    2024年02月17日
    浏览(34)
  • 与AI合作 -- 写一个modern c++单例工厂

    目录 前言 提问 bard给出的答案 AI答案的问题 要求bard改进  人类智能 AI VS 人类 通过本文读者可以学到modern C++单例模式+工厂模式的混合体,同时也能看到:如今AI发展到了怎样的智能程度?怎样让AI帮助我们快速完成实现头脑中的想法?以及这样的智能程度会不会让程序员失

    2024年02月21日
    浏览(25)
  • 【C++ 设计模式】策略模式与简单工厂模式的结合

    在软件设计中,常常会遇到需要根据不同情况选择不同算法或行为的情况。策略模式和简单工厂模式是两种常见的设计模式,它们分别解决了对象行为的抽象和对象创建的抽象问题。在某些情况下,将这两种模式结合起来可以更好地满足实际需求,提高代码的灵活性和可维护

    2024年03月16日
    浏览(41)
  • 【设计模式】Head First 设计模式——工厂方法模式 C++实现

    设计模式最大的作用就是在变化和稳定中间寻找隔离点,然后分离它们,从而管理变化。将变化像小兔子一样关到笼子里,让它在笼子里随便跳,而不至于跳出来把你整个房间给污染掉。 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使得一个类的实

    2024年02月10日
    浏览(35)
  • 【C++设计模式】用简单工厂模式实现按汽车重量输出汽车类型

    2023年8月24日,周四凌晨  

    2024年02月11日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包