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


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

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

    方法介绍

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

    1. -u symbol
    2. --undefined=symbol

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

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

    怎么找符号

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

    pay_handler.cpp文件如下:

    1. #include "msg_handler.h"
    2. #include
    3. class PayHandler {
    4. public:
    5. PayHandler() {
    6. register_msg_handler("pay", PayHandler::handle);
    7. }
    8. static bool handle(std::string_view msg_data) {
    9. printf("pay handle\n");
    10. return true;
    11. }
    12. };
    13. static PayHandler pay_handler;

    假设我们要找handle函数的符号,整个流程如下:
    (1)编译pay_handler.cpp为目标文件,符号信息都存储在目标文件里

    $ g++ -c pay_handler.cpp
    (2)使用nm获取所有符号,结合grep命令搜索疑似符号
    1. $ nm -g pay_handler.o | grep handle
    2.                U _Z20register_msg_handlerPKcSt8functionIFbSt17basic_string_viewIcSt11char_traitsIcEEEE
    3. 0000000000000000 W _ZN10PayHandler6handleESt17basic_string_viewIcSt11char_traitsIcEE
    4. 0000000000000000 W _ZNSt17_Function_handlerIFbSt17basic_string_viewIcSt11char_traitsIcEEEPS4_E10_M_managerERSt9_Any_dataRKS7_St18_Manager_operation
    5. 0000000000000000 W _ZNSt17_Function_handlerIFbSt17basic_string_viewIcSt11char_traitsIcEEEPS4_E9_M_invokeERKSt9_Any_dataOS3_
    (3)使用c++filt命令还原改编之前的符号,确定最终的符号
    1. $ c++filt _ZN10PayHandler6handleESt17basic_string_viewIcSt11char_traitsIcEE
    2. PayHandler::handle(std::basic_string_view<char, std::char_traits<char> >)
    最终的CMakeList.txt如下:
    1. cmake_minimum_required (VERSION 3.24.0)
    2. project(main)
    3. add_library(payhandler STATIC pay_handler.cpp)
    4. add_library(msghandler STATIC msg_handler.cpp)
    5. add_executable(${PROJECT_NAME} main.cpp)
    6. target_link_libraries(
    7. ${PROJECT_NAME}
    8. msghandler
    9. payhandler
    10. -u_ZN10PayHandler6handleESt17basic_string_viewIcSt11char_traitsIcEE
    11. )

    优劣分析

    两个方面分析:

    • 跨编译器

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

    • 对文件大小的影响

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

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

    附代码

    msg_handler.h

    1. #include
    2. #include
    3. using MsgHandler = std::function<bool(std::string_view msg_data)>;
    4. // 注册消息处理器
    5. void register_msg_handler(const char *msg_type, MsgHandler handler);
    6. // 获取指定消息类型的处理器
    7. MsgHandler* get_msg_handler(const char *msg_type);

    msg_handler.cpp

    1. #include
    2. #include
    3. #include "msg_handler.h"
    4. static std::map& get_map() {
    5. static std::map map_handlers;
    6. return map_handlers;
    7. }
    8. void register_msg_handler(const char *msg_type, MsgHandler handler) {
    9. get_map()[msg_type] = handler;
    10. }
    11. MsgHandler* get_msg_handler(const char *msg_type) {
    12. auto& m = get_map();
    13. auto it = m.find(msg_type);
    14. if (it != m.end()) {
    15. return &it->second;
    16. }
    17. else {
    18. return nullptr;
    19. }
    20. }

    pay_handler.cpp

    1. #include "msg_handler.h"
    2. #include
    3. class PayHandler {
    4. public:
    5. PayHandler() {
    6. register_msg_handler("pay", PayHandler::handle);
    7. }
    8. static bool handle(std::string_view msg_data) {
    9. printf("pay handle\n");
    10. return true;
    11. }
    12. };
    13. static PayHandler pay_handler

    main.cpp

    1. #include "msg_handler.h"
    2. #include
    3. int main() {
    4. MsgHandler* handle = get_msg_handler("pay");
    5. if (handle) {
    6. (*handle)("test data");
    7. }
    8. else {
    9. printf("not found\n");
    10. }
    11. return 0;
    12. }
  • 相关阅读:
    以解析csv数据为例,讨论string、char[]、stream 不同类型来源是否能进行高性能读取解析封装可能性
    怎样寻找服务器大文件的方法
    微软宣布即将停止对 Visual Studio 旧版本的支持
    东华大学Linux实验一
    (html+CSS)垂直居中
    前端面试常见错误
    祥云杯2022 pwn - sandboxheap
    Kotlin - 挂起函数 suspend
    尚硅谷设计模式学习(十六)访问者模式
    OIDC 在 Authing 控制台的配置流程 | 认证(二)
  • 原文地址:https://blog.csdn.net/qq_17291647/article/details/134389707