上篇文章《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
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 >)
最终的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
#include
using MsgHandler = std::function;
// 注册消息处理器
void register_msg_handler(const char *msg_type, MsgHandler handler);
// 获取指定消息类型的处理器
MsgHandler* get_msg_handler(const char *msg_type);
msg_handler.cpp
#include
pay_handler.cpp
#include "msg_handler.h"
#include
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
int main() {
MsgHandler* handle = get_msg_handler("pay");
if (handle) {
(*handle)("test data");
}
else {
printf("not found\n");
}
return 0;
}