Reflection for aggregates

// Copyright 2020 Andrzej Krzemienski.
//
// This shows how to implement a basic reflection for aggregate
// types in C++17.
// This code has been inspired by Precise and Flat Reflection
// library by Antony Polukhin at:
// https://github.com/apolukhin/magic_get

#include <cassert>
#include <iostream>
#include <string>
#include <type_traits>
#include <tuple>

struct Record
{
  std::string name;
  int age;
  double salary;
};

struct Point
{
  int x;
  int y;
};

struct init
{
  template <typename T>
  operator T(); // never defined
};

template <unsigned I>
struct tag : tag<I - 1> {};

template <>
struct tag<0> {};

template <typename T>
constexpr auto size_(tag<4>) 
  -> decltype(T{init{}, init{}, init{}, init{}}, 0u)
{ return 4u; }
 
template <typename T>
constexpr auto size_(tag<3>) 
  -> decltype(T{init{}, init{}, init{}}, 0u)
{ return 3u; }
 
template <typename T>
constexpr auto size_(tag<2>) 
  -> decltype(T{init{}, init{}}, 0u)
{ return 2u; }
 
template <typename T>
constexpr auto size_(tag<1>) 
  -> decltype(T{init{}}, 0u)
{ return 1u; }
 
template <typename T>
constexpr auto size_(tag<0>) 
  -> decltype(T{}, 0u)
{ return 0u; }
 
template <typename T>
constexpr size_t size() 
{ 
  static_assert(std::is_aggregate_v<T>);
  return size_<T>(tag<4>{}); // highest supported number 
}

template <typename T, typename F>
void for_each_member(T const& v, F f)
{
  static_assert(std::is_aggregate_v<T>);

  if constexpr (size<T>() == 4u)
  {
    const auto& [m0, m1, m2, m3] = v;
    f(m0); f(m1); f(m2); f(m3);
  }
  else if constexpr (size<T>() == 3u)
  {
    const auto& [m0, m1, m2] = v;
    f(m0); f(m1); f(m2);
  }
  else if constexpr (size<T>() == 2u)
  {
    const auto& [m0, m1] = v;
    f(m0); f(m1);
  }
  else if constexpr (size<T>() == 1u)
  {
    const auto& [m0] = v;
    f(m0);
  }
}

template <typename T>
auto as_tuple(T const& v)
{
  static_assert(std::is_aggregate_v<T>);

  if constexpr (size<T>() == 4u)
  {
    const auto& [m0, m1, m2, m3] = v;
    return std::tie(m0, m1, m2, m3);
  }
  else if constexpr (size<T>() == 3u)
  {
    const auto& [m0, m1, m2] = v;
    return std::tie(m0, m1, m2);
  }
  else if constexpr (size<T>() == 2u)
  {
    const auto& [m0, m1] = v;
    return std::tie(m0, m1);
  }
  else if constexpr (size<T>() == 1u)
  {
    const auto& [m0] = v;
    return std::tie(m0);
  }
}

int main()
{
 Point pt{2, 3};
  Record rec {"Baggins", 111, 999.99};
 
  auto print = [](auto const& member) {
    std::cout << member << " ";
  };  

  for_each_member(rec, print);
  for_each_member(pt, print);
    
  assert(as_tuple(Point{1, 1}) < as_tuple(pt)); 
}