Converting 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...);
};