聊聊c++的反射
反射是什么?
就是你可以用一个字符串去修改某个实例的某个字段。
RTTR
这个框架 对低版本的gcc支持还不错,但是最新的0.9.6版本是2018年的。7年多过去了,都没有更新的版本。我的本地gcc版本是14。编译起来一堆问题。实在不推荐高版本环境使用。
boost.pfr
#include <boost/pfr.hpp>
#include <iostream>
#include <string>struct Person {// the order can not changestd::string name; // public (必须是 public 才能被 PFR 访问)int age; // the order can not change
};int main() {Person p{"Alice", 30};std::cout << "Fields of Person:\n";boost::pfr::for_each_field(p, [](const auto& field,std::size_t idx) {std::cout << " [" << idx << "] = " << field << "\n";});std::cout << "Number of fields: " << boost::pfr::tuple_size_v<Person> << "\n";// 按索引访问(name 是第0个,age 是第1个)std::cout << "Name: " << boost::pfr::get<0>(p) << "\n";std::cout << "Age: " << boost::pfr::get<1>(p) << "\n";// 修改 ageboost::pfr::get<1>(p) = 35;std::cout << "after change Age: " << boost::pfr::get<1>(p) << "\n";return 0;
}
能正常运行,但是怎么说呢,只能根据编号去修改字段。有点鸡肋。
boost::pfr::get<1>(p) = 35;
而且如上的代码,get里面只能写常量,不能写变量。也就是说想通过filed_name找到编号,然后再把编号传到get里面瑟吉欧不行的。
通过json序列化达到反射的目标
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>struct Person {std::string name;int age{};double salary{};
};NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Person, name, age, salary)bool update_field_via_json(Person& p, const std::string& field, const std::string& value_str) {nlohmann::json j = p;if (!j.contains(field)) {return false;}auto& orig_val = j[field];try {if (orig_val.is_string()) {j[field] = value_str;} else if (orig_val.is_number_integer()) {j[field] = std::stoi(value_str);} else if (orig_val.is_number_float()) {j[field] = std::stod(value_str);} else {return false;} catch (...) {return false;}p = j.get<Person>();return true;
}int main() {Person p{"Alice", 30, 8888.88};std::cout << "Before: " << p.name << ", " << p.age << "\n";if (update_field_via_json(p, "age", "35")) {std::cout << "After: " << p.name << ", " << p.age << "\n"; // age=35}update_field_via_json(p, "name", "Alice Cooper");std::cout << "Name now: " << p.name << "\n";return 0;
}
这种转换 肯定性能不够强,但是一般情况下,如果只是为了修改某个实体的某个字段,也不需要多强的性能
模板方法升级
刚才上面的person可以运行,如果我再加一个Car呢?
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>// 你的结构体
struct Person {std::string name;int age{};double salary{};
};// 自动生成 to_json / from_json
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Person, name, age, salary)// 再定义一个新结构体,无需额外代码!
struct Config {bool debug{};int max_connections{};std::string log_level{};
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Config, debug, max_connections, log_level);// ====== 通用更新函数(只需写一次!) ======
template<typename T>
bool update_field_via_json(T& obj, const std::string& field, const std::string& value_str) {nlohmann::json j = obj; // 序列化当前对象if (!j.contains(field)) {return false; // 字段不存在}auto& orig_val = j[field];try {if (orig_val.is_string()) {j[field] = value_str;} else if (orig_val.is_number_integer()) {j[field] = std::stoll(value_str); // 更安全:支持 long long} else if (orig_val.is_number_float()) {j[field] = std::stod(value_str);} else if (orig_val.is_boolean()) {// 支持 "true"/"false", "1"/"0"std::string lower = value_str;std::transform(lower.begin(), lower.end(), lower.begin(),[](unsigned char c) { return std::tolower(c); });if (lower == "true" || lower == "1") {j[field] = true;} else if (lower == "false" || lower == "0") {j[field] = false;} else {return false; // 无效布尔值}} else {return false; // 不支持 array/object/null 等复杂类型}} catch (...) {return false; // 转换异常(如 stoi("abc"))}// 反序列化回对象try {obj = j.get<T>();return true;} catch (...) {return false; // 反序列化失败(理论上不应发生)}
}// ============ 测试 ============
int main() {Person p{"Alice", 30, 8888.88};std::cout << "Before: " << p.name << ", " << p.age << "\n";if (update_field_via_json(p, "age", "35")) {std::cout << "After age update: " << p.age << "\n";}update_field_via_json(p, "name", "Alice Cooper");std::cout << "Name now: " << p.name << "\n";Config cfg{false, 100, "info"};update_field_via_json(cfg, "debug", "true");update_field_via_json(cfg, "max_connections", "200");std::cout << "Config: debug=" << cfg.debug<< ", max_conn=" << cfg.max_connections << "\n";return 0;
}