#include <iostream>
#include <tuple>
using namespace std;

template<
	typename Callable,
	typename Tuple,
	size_t... Indexes
>
constexpr void tuple_for_each(Tuple&& t, Callable&& f, std::index_sequence<Indexes...>)
{
	// ideone only has C++14 :(
	int _[] = {(f(std::get<Indexes>(t)),0)...};
}

template<
	typename Callable,
	typename... Args,
	template<typename...> typename Tuple,
	typename Is = std::make_index_sequence<sizeof...(Args)>
>
constexpr void tuple_for_each(Tuple<Args...>&& t, Callable&& f)
{
	tuple_for_each(t, f, Is{});
}

template<typename T>
struct ReflectionLayout
{
	static constexpr auto GetLayout()
	{
		return std::make_tuple(
			std::make_tuple(
				"#Unknown", "Unknown"
			)
		);
	}
};

template<typename T>
struct Reflection
{
	static constexpr bool has_reflection_info = false;
	static constexpr char const* name{ "Unknown" };
	static constexpr auto layout() { return ReflectionLayout<T>::GetLayout(); }
	static constexpr bool is_complex{ false };
};

#define REGISTER_REFLECTION_INFO(Class) \
template<> \
struct Reflection<Class> \
{ \
	static constexpr bool has_reflection_info = true; \
	static constexpr char const* name{ #Class }; \
	static constexpr auto layout() { return ReflectionLayout<Class>::GetLayout(); } \
	static constexpr bool is_complex{ true }; \
};

#define REGISTER_REFLECTION_LAYOUT(Class) \
template<> \
struct ReflectionLayout<Class> \
{ \
	using Type = Class; \
	static constexpr auto GetLayout() \
	{ \
		return std::make_tuple(

#define MEMBER(Name) std::make_tuple(#Name, &Type::Name)

#define END_REFLECTION_LAYOUT(Class) \
		); \
	} \
}; \
REGISTER_REFLECTION_INFO(Class)

struct namedValue
{
	string name;
	int x;
};
REGISTER_REFLECTION_LAYOUT(namedValue)
	MEMBER(name),
	MEMBER(x)
END_REFLECTION_LAYOUT(namedValue)

template<typename T>
void printInfo(T val)
{
	tuple_for_each(Reflection<T>::layout(),[&](auto&& x) {
		// auto [] is C++17, so we have to do this instead
		auto& name = get<0>(x);
		auto& ptr = get<1>(x);
		cout << name << ": " << val.*ptr << endl;
	});
}

int main() {
	printInfo(namedValue{"numEggs", 37});
	return 0;
}