【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;
}
相关推荐
柠石榴4 分钟前
【练习】【回溯No.1】力扣 77. 组合
c++·算法·leetcode·回溯
王老师青少年编程5 分钟前
【GESP C++八级考试考点详细解读】
数据结构·c++·算法·gesp·csp·信奥赛
计算机毕设定制辅导-无忧学长23 分钟前
Maven 基础环境搭建与配置(一)
java·maven
彳卸风29 分钟前
Unable to parse timestamp value: “20250220135445“, expected format is
开发语言
dorabighead1 小时前
JavaScript 高级程序设计 读书笔记(第三章)
开发语言·javascript·ecmascript
css趣多多1 小时前
案例自定义tabBar
前端
风与沙的较量丶1 小时前
Java中的局部变量和成员变量在内存中的位置
java·开发语言
m0_748251721 小时前
SpringBoot3 升级介绍
java
水煮庄周鱼鱼2 小时前
C# 入门简介
开发语言·c#
澄澈天空2 小时前
C++ MFC添加RichEditControl控件后,程序启动失败
c++·mfc