如何在程序启动时通过类收集编译期常量字符串信息?
解决方案
一、基于类的运行期注册方案(满足main启动时可用)
核心思路是利用C++全局对象构造函数在main执行前调用的特性,结合单例Holder类实现字符串的提前注册:
1. Holder类实现
// Holder.h #pragma once #include <vector> #include <string> class Holder { public: // 静态注册方法,供外部组件调用 static void registerInfo(const std::string& info) { getInstance().data_list.push_back(info); } // 获取所有已注册的字符串列表 static const std::vector<std::string>& getRegisteredInfos() { return getInstance().data_list; } private: // 私有构造,确保单例 Holder() = default; Holder(const Holder&) = delete; Holder& operator=(const Holder&) = delete; // 单例实例获取逻辑 static Holder& getInstance() { static Holder instance; return instance; } // 存储注册字符串的容器 std::vector<std::string> data_list; };
2. 组件侧注册实现
在需要注册字符串的组件中,定义全局静态实例,借助其构造函数完成注册:
// Comp.h #pragma once #include "Holder.h" class Comp { public: Comp() { Holder::registerInfo("some information from Comp"); } }; // 全局实例触发构造,在main前完成注册 static Comp comp_instance;
3. main函数中使用
#include <iostream> #include "Holder.h" int main() { const auto& infos = Holder::getRegisteredInfos(); for (const auto& info : infos) { std::cout << info << std::endl; } return 0; }
注意:全局对象的构造顺序不固定,若多个组件存在依赖关系需额外处理;多线程环境下动态注册需加锁(但main前初始化通常为单线程,无需额外处理)。
二、编译期注册方案
若要完全在编译期完成字符串收集,避免运行时初始化的不确定性,可采用以下方案:
1. 模板元编程+全局变量初始化
// Holder.h #pragma once #include <vector> #include <string> namespace detail { // 全局容器存储编译期注册的字符串 inline std::vector<std::string> compile_time_infos; // 模板类触发注册动作 template<const char* Str> struct Register { Register() { compile_time_infos.push_back(Str); } }; } class Holder { public: static const std::vector<std::string>& getCompileTimeInfos() { return detail::compile_time_infos; } }; // 宏简化注册流程 #define REGISTER_COMPILE_INFO(STR) \ inline const char str_##__LINE__[] = STR; \ detail::Register<str_##__LINE__> reg_##__LINE__;
2. 组件侧注册方式
// Comp.cpp #include "Holder.h" // 编译期注册字符串 REGISTER_COMPILE_INFO("compile time info from Comp");
3. 更纯粹的C++20编译期方案
利用constexpr和模板拼接,完全在编译期构建字符串数组:
// Holder.h #pragma once #include <array> #include <string_view> // 单个注册项的容器 template<std::string_view... Strs> struct CompileTimeHolder { static constexpr std::array<std::string_view, sizeof...(Strs)> infos = {Strs...}; }; // 拼接多个注册项的模板逻辑 template<typename H1, typename H2> struct MergeHolder; template<std::string_view... S1, std::string_view... S2> struct MergeHolder<CompileTimeHolder<S1...>, CompileTimeHolder<S2...>> { using type = CompileTimeHolder<S1..., S2...>; }; // 宏定义单个注册 #define REGISTER_COMPILE_STR(STR) \ using CompReg_##__LINE__ = CompileTimeHolder<STR>; // 宏合并所有注册项 #define MERGE_ALL_REGISTERS(...) \ using AllRegistrations = MergeHolder<__VA_ARGS__>::type;
使用示例:
// Comp1.cpp #include "Holder.h" REGISTER_COMPILE_STR("info 1") // Comp2.cpp #include "Holder.h" REGISTER_COMPILE_STR("info 2") // main.cpp #include <iostream> #include "Holder.h" MERGE_ALL_REGISTERS(CompReg_1, CompReg_2) int main() { for (const auto& info : AllRegistrations::infos) { std::cout << info << std::endl; } return 0; }
该方案无运行时开销,所有字符串在编译期完成收集,但需要C++20及以上版本支持。
内容的提问来源于stack exchange,提问作者Ahmad Mansoori
相关产品推荐
相关产品推荐

