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

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

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

方法介绍

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

-u symbol
--undefined=symbol

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

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

怎么找符号

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

pay_handler.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为目标文件,符号信息都存储在目标文件里

$ g++ -c pay_handler.cpp
(2)使用nm获取所有符号,结合grep命令搜索疑似符号
$ 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_
(3)使用c++filt命令还原改编之前的符号,确定最终的符号
$ c++filt _ZN10PayHandler6handleESt17basic_string_viewIcSt11char_traitsIcEE
PayHandler::handle(std::basic_string_view<char, std::char_traits<char> >)
最终的CMakeList.txt如下:
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

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

pay_handler.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

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值