【C++】替代--whole-archive的一种方式

上篇文章《C++自动注册的工厂与--whole-archive》提到了--whole-archive选项在自动工厂示例的必要,"貌似也没其他方法了"。

这篇文章介绍另一种可以替代的方式,并分析其优缺点,采用的代码示例同上篇文章。文章最后附代码。

方法介绍

ld链接器提供了另外一组选项解决符号依赖的问题。

diff 复制代码
-u symbol
--undefined=symbol

上面的选项用来告诉链接器symbol符号在最终的输出文件里是未定义的,需要从其他模块拉进去。

这样我们就可以指定依赖的符号,让链接器强制将pay_handler.cpp生成的目标文件链接到可执行文件。

怎么找符号

由于C++允许函数重载,函数名不具有唯一性,编译器需要对名字进行改编,也就是name mangling。所以不能简单的将函数名写在链接选项后面,需要指定实际改编后的符号。

pay_handler.cpp文件如下:

cpp 复制代码
#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;

假设我们要找handle函数的符号,整个流程如下:

(1)编译pay_handler.cpp为目标文件,符号信息都存储在目标文件里

shell 复制代码
$ g++ -c pay_handler.cpp
go 复制代码
(2)使用nm获取所有符号,结合grep命令搜索疑似符号
properties 复制代码
$ nm -g pay_handler.o | grep handle
               U _Z20register_msg_handlerPKcSt8functionIFbSt17basic_string_viewIcSt11char_traitsIcEEEE
0000000000000000 W _ZN10PayHandler6handleESt17basic_string_viewIcSt11char_traitsIcEE
0000000000000000 W _ZNSt17_Function_handlerIFbSt17basic_string_viewIcSt11char_traitsIcEEEPS4_E10_M_managerERSt9_Any_dataRKS7_St18_Manager_operation
0000000000000000 W _ZNSt17_Function_handlerIFbSt17basic_string_viewIcSt11char_traitsIcEEEPS4_E9_M_invokeERKSt9_Any_dataOS3_
go 复制代码
(3)使用c++filt命令还原改编之前的符号,确定最终的符号
cpp 复制代码
$ c++filt _ZN10PayHandler6handleESt17basic_string_viewIcSt11char_traitsIcEE
PayHandler::handle(std::basic_string_view<char, std::char_traits<char> >)
go 复制代码
最终的CMakeList.txt如下:
nginx 复制代码
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
    payhandler
    -u_ZN10PayHandler6handleESt17basic_string_viewIcSt11char_traitsIcEE
)

优劣分析

两个方面分析:

  • 跨编译器

cmake内置支持--whole-archive,所以可以跨编译器,而本篇介绍的选项需要指定符号,而符号是跟具体编译器相关的,所以不具备跨编译器的能力。

  • 对文件大小的影响

--whole-archive会将静态库的所有目标文件链接进来,最终的输出文件可能会比较大;而本篇介绍的选项则只加载符号所依赖的目标文件。

所以要综合考虑以上两个方面选择适合的选项。

附代码

msg_handler.h

cpp 复制代码
#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

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

pay_handler.cpp

cpp 复制代码
#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

main.cpp

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;
}
相关推荐
wand codemonkey14 分钟前
SpringbootWeb【入门】+MySQL【安装】+【DataDrip安装 】+【连接MySQL】
java·mysql·mybatis
Highcharts.js1 小时前
缺失数据可视化图表开发实战|Highcharts创建人员出生统计面积图表示例
开发语言·前端·javascript·信息可视化·highcharts·图表开发
测试员周周6 小时前
【Appium 系列】第16节-WebView-H5上下文切换 — 混合应用的自动化难点
运维·开发语言·人工智能·功能测试·appium·自动化·测试用例
LaughingZhu8 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
怕浪猫8 小时前
Electron 开发实战(一):从零入门核心基础与环境搭建
前端·electron·ai编程
Mahir088 小时前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
杜子不疼.8 小时前
【C++ AI 大模型接入 SDK】 - DeepSeek 模型接入(上)
开发语言·c++·chatgpt
加号38 小时前
【C#】 串口通信技术深度解析及实现
开发语言·c#
小鹏linux9 小时前
Ubuntu 22.04 部署开源免费具有精美现代web页面的Casdoor账号管理系统
linux·前端·ubuntu·开源·堡垒机
sycmancia9 小时前
Qt——编辑交互功能的实现
开发语言·qt