* What type of problems static reflection could solve, in general?
* Are there specific cases and / or situations where static reflection could resolve such case, even simplify an unnecessary complexity?
* What type of problems static reflection could solve, in general?
* Are there specific cases and / or situations where static reflection could resolve such case, even simplify an unnecessary complexity?
template<typename T>
std::string to_json(const T& obj) {
std::ostringstream oss;
constexpr auto type_info = reflect(T);
if constexpr (type_info.is_fundamental()) {
// Fundamental types (int, float, etc.)
if constexpr (std::is_same_v<T, bool>) {
oss << (obj ? "true" : "false");
} else if constexpr (std::is_arithmetic_v<T>) {
oss << obj;
} else if constexpr (std::is_same_v<T, std::string>) {
oss << "\"" << obj << "\"";
}
}
else if constexpr (type_info.is_enum()) {
// Enums
oss << "\"" << type_info.enum_name(obj) << "\"";
}
else if constexpr (type_info.is_array() || std::is_same_v<T, std::vector<typename T::value_type>>) {
// Arrays and vectors
oss << "[";
bool first = true;
for (const auto& elem : obj) {
if (!first) oss << ",";
oss << to_json(elem);
first = false;
}
oss << "]";
}
else if constexpr (std::is_same_v<T, std::map<typename T::key_type, typename T::mapped_type>>) {
// Maps
oss << "{";
bool first = true;
for (const auto& [key, value] : obj) {
if (!first) oss << ",";
oss << "\"" << key << "\":" << to_json(value);
first = false;
}
oss << "}";
}
else if constexpr (type_info.is_class()) {
// Classes and structs
oss << "{";
bool first = true;
for (const auto& member : type_info.members()) {
if (!first) oss << ",";
oss << "\"" << member.name() << "\":" << to_json(member.get(obj));
first = false;
}
oss << "}";
}
return oss.str();
}
enum class Color { Red, Green, Blue };
struct Address {
std::string street;
std::string city;
int zip;
};
struct Person {
std::string name;
int age;
double height;
Color favorite_color;
Address address;
std::vector<std::string> hobbies;
std::map<std::string, int> scores;
};
int main() {
Person person {
"John Doe",
30,
175.5,
Color::Blue,
{"123 Main St", "Anytown", 12345},
{"reading", "hiking", "coding"},
{{"math", 95}, {"history", 88}, {"science", 92}}
};
std::cout << to_json(person) << std::endl;
return 0;
}
I have a scripting layer in a game that needs to set properties in a C++ Model. I used a single Boost.Describe macro per struct and a generic get/set property. It worked very well and made me get rid of a lot of boilerplate.
https://www.boost.org/doc/libs/develop/libs/describe/doc/htm...
From there a lot of functionality for making statements about what something is has been bolted on to the side of what is really a very sophisticated type-aware preprocessor, and it shows. It's very painful to use it when you go from the core C++ language to the template, because the semantics are very different. Which the same can be said of C++->Preprocessor.
I think proper reflection should be a core part of the language that can be evaluated at compile-time with simple constructs. It's hard to articulate specifics but I think this proposal greatly simplifies working with type metadata to the degree that it's an approachable problem without using someone else's library. This proposal seems to do that in my opinion, and I think even some of the weaker programmers I've worked with could use this effectively.