3 ms·
Here are some examples from the linked paper * Converting enum values to strings, and vice versa * Parsing command line arguments from a struct definition (li
by edflsafoiewq 2y ago
Here are some examples from the linked paper
* Converting enum values to strings, and vice versa
* Parsing command line arguments from a struct definition (like Rust's clap)
* Simple definition of tuple and variant types, without the complex metaprogramming tricks currently used
* Automatic conversion between struct-of-arrays and array-of-structs form
* A "universal formatter" that can print any struct with all its fields
* Hashing a struct by iterating over its fields
* Convert between a struct and tuple, tuple concatenation, named tuples
- greenavocado 2y agoConverting enum values to strings, and vice versa enum class Color { Red, Green, Blue }; template<typename E> std::string enum_to_string(E value) { constexpr auto enum_info = reflect(E); for (const auto& enumerator : enum_info.enumerators()) { if (enumerator.value() == value) { return std::string(enumerator.name()); } } return "Unknown"; } template<typename E> E string_to_enum(const std::string& str) { constexpr auto enum_info = reflect(E); for (const auto& enumerator : enum_info.enumerators()) { if (enumerator.name() == str) { return enumerator.value(); } } throw std::invalid_argument("Invalid enum string"); } Parsing command line arguments from a struct definition struct CLIOptions { std::string input_file; int num_threads = 1; bool verbose = false; }; template<typename T> T parse_cli_args(int argc, char* argv[]) { T options; constexpr auto struct_info = reflect(T); for (int i = 1; i < argc; i++) { std::string arg = argv[i]; for (const auto& member : struct_info.members()) { if (arg == "--" + std::string(member.name())) { if (member.type() == typeid(bool)) { member.set(options, true); } else if (i + 1 < argc) { member.set(options, std::string(argv[++i])); } break; } } } return options; } Simple definition of tuple and variant types // Common data structure used in examples below struct Person { std::string name; int age; double height; }; // Tuple, without reflection int main() { std::tuple<std::string, int, double> person_tuple{"John Doe", 30, 175.5}; std::cout << "Name: " << std::get<0>(person_tuple) << std::endl; std::cout << "Age: " << std::get<1>(person_tuple) << std::endl; std::cout << "Height: " << std::get<2>(person_tuple) << std::endl; Person p{"Jane Doe", 25, 165.0}; auto p_tuple = std::make_tuple(p.name, p.age, p.height); return 0; } // Tuple, with reflection int main() { std::tuple<std::string, int, double> person_tuple{"John Doe", 30, 175.5}; std::apply([](const auto&... args) { (..., (std::cout << reflect(args).name() << ": " << args << std::endl)); }, person_tuple); Person p{"Jane Doe", 25, 165.0}; auto p_tuple = std::apply([&p](auto... members) { return std::make_tuple(members.get(p)...); }, reflect(Person).members()); return 0; } // Variant, without reflection int main() { std::variant<int, std::string, Person> var; var = 42; std::cout << "Variant holds: " << std::get<int>(var) << std::endl; var = "Hello, World!"; std::cout << "Variant holds: " << std::get<std::string>(var) << std::endl; var = Person{"Alice", 28, 170.0}; const auto& p = std::get<Person>(var); std::cout << "Variant holds Person: " << p.name << ", " << p.age << ", " << p.height << std::endl; std::visit([](const auto& v) { using T = std::decay_t<decltype(v)>; if constexpr (std::is_same_v<T, int>) std::cout << "Int: " << v << std::endl; else if constexpr (std::is_same_v<T, std::string>) std::cout << "String: " << v << std::endl; else if constexpr (std::is_same_v<T, Person>) std::cout << "Person: " << v.name << std::endl; }, var); return 0; } // Variant, with reflection int main() { std::variant<int, std::string, Person> var; var = 42; std::cout << "Variant holds: " << std::get<int>(var) << std::endl; var = "Hello, World!"; std::cout << "Variant holds: " << std::get<std::string>(var) << std::endl; var = Person{"Alice", 28, 170.0}; std::visit([](const auto& v) { constexpr auto type_info = reflect(std::decay_t<decltype(v)>); std::cout << "Variant holds " << type_info.name() << ": "; if constexpr (type_info.is_class()) { for (const auto& member : type_info.members()) { std::cout << member.name() << ": " << member.get(v) << ", "; } } else { std::cout << v; } std::cout << std::endl; }, var); return 0; } Automatic conversion between struct-of-arrays and array-of-structs template<typename Struct, size_t N> auto soa_to_aos(const StructOfArrays<Struct, N>& soa) { std::array<Struct, N> aos; constexpr auto struct_info = reflect(Struct); for (size_t i = 0; i < N; ++i) { for (const auto& member : struct_info.members()) { member.set(aos[i], soa.get(member.name())[i]); } } return aos; } template<typename Struct, size_t N> auto aos_to_soa(const std::array<Struct, N>& aos) { StructOfArrays<Struct, N> soa; constexpr auto struct_info = reflect(Struct); for (size_t i = 0; i < N; ++i) { for (const auto& member : struct_info.members()) { soa.get(member.name())[i] = member.get(aos[i]); } } return soa; } Universal formatter: template<typename T> std::string format(const T& obj) { std::ostringstream oss; constexpr auto type_info = reflect(T); oss << type_info.name() << " {\n"; for (const auto& member : type_info.members()) { oss << " " << member.name() << ": " << member.get(obj) << ",\n"; } oss << "}"; return oss.str(); } Hashing a struct by iterating over its fields: template<typename T> size_t hash_struct(const T& obj) { size_t hash = 0; constexpr auto type_info = reflect(T); for (const auto& member : type_info.members()) { hash ^= std::hash<decltype(member.get(obj))>{}(member.get(obj)) + 0x9e3779b9 + (hash << 6) + (hash >> 2); } return hash; } Convert between struct and tuple, tuple concatenation, named tuples: // Struct to tuple template<typename Struct> auto struct_to_tuple(const Struct& s) { return std::apply([&](auto&&... members) { return std::make_tuple(members.get(s)...); }, reflect(Struct).members()); } // Tuple to struct template<typename Struct, typename Tuple> Struct tuple_to_struct(const Tuple& t) { Struct s; std::apply([&](auto&&... members) { ((members.set(s, std::get<members.index()>(t))), ...); }, reflect(Struct).members()); return s; } // Tuple concatenation template<typename... Tuples> auto tuple_concat(Tuples&&... tuples) { return std::tuple_cat(std::forward<Tuples>(tuples)...); } // Named tuple template<typename... Members> struct NamedTuple { REFLECT_NAMED_MEMBERS(Members...); };
- fluoridation 2y agoThis is terrible. You can't just do Enum::Member.str or something?
- Aardwolf 2y agoOuch, I first thought this was the example of how to do these things _before_ reflection is introduced to C++ The for loop required to do enum to string really makes it
- einpoklum 2y agoYou can use `.name()` and that also works fine. Remember std::string is a heap-based thing.
- kllrnohj 2y agoThat'll inevitably be a utility function that exists, but C++ generally prefers broadly useful language primitives over single-case helpers
- j16sdiz 2y agoThis is how C++ language usually works. Just some primitive for building libraries on. Expect some library change come later.
- coopierez 2y ago