【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;
}
相关推荐
猪猪拆迁队18 分钟前
2025年终总结-都在喊前端已死,这一年我的焦虑、挣扎与重组:AI 时代如何摆正自己的位置
前端·后端·ai编程
寻星探路20 分钟前
【算法专题】滑动窗口:从“无重复字符”到“字母异位词”的深度剖析
java·开发语言·c++·人工智能·python·算法·ai
程序员小白条20 分钟前
面试 Java 基础八股文十问十答第八期
java·开发语言·数据库·spring·面试·职场和发展·毕设
Dxy123931021620 分钟前
python连接minio报错:‘SSL routines‘, ‘ssl3_get_record‘, ‘wrong version number‘
开发语言·python·ssl
❆VE❆24 分钟前
WebSocket与SSE深度对比:技术差异、场景选型及一些疑惑
前端·javascript·网络·websocket·网络协议·sse
ConardLi25 分钟前
SFT、RAG 调优效率翻倍!垂直领域大模型评估实战指南
前端·javascript·后端
大王小生25 分钟前
C# CancellationToken
开发语言·c#·token·cancellation
listhi52025 分钟前
基于C#实现屏幕放大镜功能
开发语言·c#
萤丰信息26 分钟前
从 “钢筋水泥” 到 “数字神经元”:北京 AI 原点社区重构城市进化新逻辑
java·大数据·人工智能·安全·重构·智慧城市·智慧园区
rgeshfgreh29 分钟前
Java高性能开发:Redis7持久化实战
前端·bootstrap·mybatis