struct Person { std::string name; int age; }; int main() { Person joe{"Joe", 30}; std::cout << to_json(joe); }
def to_json(obj): members = [] for member in obj.__dict__: members.append('"' + member + '" : ' + str(getattr(obj, member))) return '{' + ', '.join(members) + '}' class Person: def __init__(self, name, age): self.name = name self.age = age print to_json(Person("John", 30))Of course, this is a simplified version and it only handles objects.
template <typename T> std::string to_json(T const& obj) { std::vector<std::string> members; for (std::string const& member : obj.__members__()) // ??? members.push_back('"' + member + "\" : " + to_json(obj.__get__(member))); // ??? return "{" + boost::algorithm::join(members, ", ") + "}"; }We won't talk about reflection in this talk, because it's an introduction only.
class A: pass class B: pass class C: pass def register(classes): for c in classes: print "Registering " + c.__name__ register([A, B, C])This works because types are first class citizens
void register_(std::vector<...> const& classes) { for (auto c : classes) { std::cout << "Registering " << c.name() << ", which is of size " << sizeof(c) << std::endl; static_assert(sizeof(c) <= 1000, ""); } } int main() { std::vector<...> classes{A, B, C}; register_(classes); }
Type packed_triple(std::vector<Type> types) { std::sort(types.begin(), types.end(), [](auto const& a, auto const& b) { return a.alignment() > b.alignment(); }); return Type{Triple<types[0], types[1], types[2]>}; } int main() { std::vector<Type> types{A, B, C}; Packed = packed_triple(types); // get the type Packed triple{...}; // define an object of that type }Notice how this sort of computation is completely compile-time. The inputs are types and the output is a type too.
double, double, double from_spherical(double rho, double theta, double phi) { double x = rho * std::sin(theta) * std::cos(phi); double y = rho * std::sin(theta) * std::sin(phi); double z = rho * std::cos(theta); return (x, y, z); } double (x, y, z) = from_spherical(3, 16.5, 25.5);
struct { double _0; double _1; double _2; } xyz = from_spherical(3, 16.5, 25.5); double x = xyz._0, y = xyz._1, z = xyz._2;
struct { Fish _0; Cat _1; Dog _2; // notice the members have different types } animals = my_animals(); Fish fish = animals._0; Cat cat = animals._1; Dog dog = animals._2;
void register_(std::vector<...> const& classes) { for (auto c : classes) { std::cout << "Registering " << c.name() << ", which is of size " << sizeof(c) << std::endl; static_assert(sizeof(c) <= 1000, ""); } } int main() { std::vector<...> classes{A, B, C}; register_(classes); }
void register_(std::vector<std::type_info> const& classes) { for (auto c : classes) { std::cout << "Registering " << c.name() << ", which is of size " << sizeof(c) << std::endl; static_assert(sizeof(c) <= 1000, ""); } } int main() { std::vector<std::type_info> classes{typeid(A), typeid(B), typeid(C)}; register_(classes); }1. We need the size of each type, not that of std::type_info itself. 2. We can't store std::type_info in a vector, because not Copyable 3. Even if type_info provided a size() method, it would only be available at runtime.
template <typename T> struct static_type_info { using type = T; }; void register_(std::vector<...> const& classes) { for (auto c : classes) { using T = typename decltype(c)::type; std::cout << "Registering " << typeid(T).name() << ", which is of size " << sizeof(T) << std::endl; static_assert(sizeof(T) <= 1000, ""); } } int main() { std::vector<...> classes{static_type_info<A>{}, static_type_info<B>{}, static_type_info<C>{}}; register_(classes); }We're close, but for this to work we'd need to be able to store objects of different types in the vector, which we can't do.
template <typename ...TypeInfos> void register_(std::tuple<TypeInfos...> const& classes) { for (auto c : classes) { using T = typename decltype(c)::type; std::cout << "Registering " << typeid(T).name() << ", which is of size " << sizeof(T) << std::endl; static_assert(sizeof(T) <= 1000, ""); } } int main() { std::tuple<static_type_info<A>, static_type_info<B>, static_type_info<C>> classes; register_(classes); }
template <typename ...TypeInfos> void register_(std::tuple<TypeInfos...> const& classes) { auto f = [](auto c) { using T = typename decltype(c)::type; std::cout << "Registering " << typeid(C).name() << ", which is of size " << sizeof(C) << std::endl; static_assert(sizeof(C) <= 1000, ""); }; f(std::get<0>(classes)); f(std::get<1>(classes)); f(std::get<2>(classes)); // ... }Recapitulating: 1. We create a static type info struct to hold type information at compile-time 2. We create a tuple of these type infos 3. We manipulate the elements of the tuple as normal objects to achieve our goal
Equivalent to
http://ldionne.com http://github.com/ldionne http://github.com/boostorg/hana