
template<class _Ty>
struct buildpath;

template<int N, class _Path, char ...chars>
struct build_path;


//-----------------------------------------------------------------------------
// constexpr for strlen
template<char ..._chars>
struct constexpr_strlen
{
	static constexpr const int value = sizeof...(_chars);
};

//-----------------------------------------------------------------------------
// constexpr string
// holds a reference to a string and its size and length as a constexpr
template<const char *_Str, int _Sz>
struct constexpr_string
{
	static constexpr const int size = _Sz;
	static constexpr const int length = _Sz - 1;
	static constexpr const char *str = _Str;
};

//-----------------------------------------------------------------------------
// a constexpr empty string
struct constexpr_string_empty
{
	static constexpr const char *str = "";
	static constexpr const int size = 1;
	static constexpr const int length = 0;
};

//-----------------------------------------------------------------------------
// exposes name and parent of this path element
template<class _Ty, class _NameStr, class _ParPathTy>
struct PathElemInfo : _NameStr
{
	using parent = _ParPathTy;
};

//-----------------------------------------------------------------------------
// An empty path element info
struct PathElemInfoEmpty : constexpr_string_empty
{};

//-----------------------------------------------------------------------------
// An empty path
struct PathEmpty
{
	using __info = PathElemInfoEmpty;
};

//-----------------------------------------------------------------------------
// Path is used to construct path element + parents which can then be
// serialize to compile-time static strings.
template<class _Ty, class _NameStr = constexpr_string_empty, class _ParPathTy = PathEmpty>
struct Path
{
	using __type = Path<_Ty, _NameStr, _ParPathTy>;
	using __info = PathElemInfo<_Ty, _NameStr, _ParPathTy>;
	static constexpr const char *path() { return buildpath<__type>::value(); }
};

//=============================================================================
// Construct constexpr strings from paths

//-----------------------------------------------------------------------------
// Add a '.' between path elements
template<int N, class _Path, char ...chars>
struct build_path_dot
{
	static constexpr const char *value() {
		return build_path<N, _Path, '.', chars...>::value();
	}
};

//-----------------------------------------------------------------------------
// Don't add a '.' on an empty path element (the root element is always empty)
template<int N, char ...chars>
struct build_path_dot<N, PathEmpty, chars...>
{
	static constexpr const char *value() {
		return build_path<N, PathEmpty, chars...>::value();
	}
};

//-----------------------------------------------------------------------------
// Don't add a '.' at the beginning of the string
template<char ...chars>
struct build_path_dot<0, PathEmpty, chars...>
{
	static constexpr const char *value() {
		return build_path<0, PathEmpty, chars...>::value;
	}
};

//-----------------------------------------------------------------------------
// Compiler gets confused if we don't provide this one as well...
template<class _Path, char ...chars>
struct build_path_dot<0, _Path, chars...>
{
	static constexpr const char *value() {
		return build_path<0, _Path, chars...>::value();
	}
};

//-----------------------------------------------------------------------------
// Extract the Nth character from the current path element
template<int N, class _Path, char ...chars>
struct build_path
{
	static constexpr const char *value() {
		return build_path<N - 1, _Path, _Path::__info::str[N - 1], chars...>::value();
	}
};

//-----------------------------------------------------------------------------
// When a path element is depleted, add a dot and move on to the next path element
template<class _Path, char ...chars>
struct build_path<0, _Path, chars...>
{
	static constexpr const char *value() {
		return  build_path_dot<_Path::__info::parent::__info::length, typename _Path::__info::parent, chars...>::value();
	}
};

//-----------------------------------------------------------------------------
// When the last path element is depleted, build the actual string from all
// the characters we have extracted
template<char ...chars>
struct build_path<0, PathEmpty, chars...>
{
	static constexpr const int size() {
		return constexpr_strlen<chars...>::value + 1;
	}
	static const char value[size()];
};

//-----------------------------------------------------------------------------
// Allocate static storage for this path
template<char ...chars>
const char build_path<0, PathEmpty, chars...>::value[] = { chars..., 0 };


//-----------------------------------------------------------------------------
// shortcut to make usage easier
template<class _Ty>
struct buildpath {
	static constexpr const char *value() { return build_path<_Ty::__info::length, _Ty>::value(); }
};

//=============================================================================
// Sample usage

struct A;
struct B;
struct C;

template<class _NameStr, class _ParPathTy>
struct Path<A, _NameStr, _ParPathTy>
{
	using __type = Path<A, _NameStr, _ParPathTy>;
	using __info = PathElemInfo<A, _NameStr, _ParPathTy>;
	
	static constexpr const char *path() { return buildpath<__type>::value(); }

	static constexpr const char __b[] = "obj_b";
	using b = Path<B, constexpr_string<__type::__b, sizeof(__type::__b)>, __type>;

	static constexpr const char __b2[] = "obj_b2";
	using b2 = Path<B, constexpr_string<__type::__b2, sizeof(__type::__b2)>, __type>;
};

template<class _NameStr, class _ParPathTy>
struct Path<B, _NameStr, _ParPathTy>
{
	using __type = Path<B, _NameStr, _ParPathTy>;
	using __info = PathElemInfo<B, _NameStr, _ParPathTy>;
	static constexpr const char *path() { return buildpath<__type>::value(); }

	static constexpr const char __c[] = "obj_c";
	using c = Path<C, constexpr_string<__type::__c, sizeof(__type::__c)>, __type>;

	static constexpr const char __a[] = "obj_a";
	using a = Path<A, constexpr_string<__type::__a, sizeof(__type::__a)>, __type>;
};


#include <iostream>
using namespace std;
int main(int argc, const char *argv[])
{
	using Q = Path<A>;
	cout << "root: " << Q::path() << endl;
	cout << "b: " << Q::b::path() << endl;
	cout << "b.c: " << Q::b::c::path() << endl;

	// make sure we can have multiple identical types under the same path
	cout << "b2.c: " << Q::b2::c::path() << endl;

	// make sure we can have recursive paths as well
	cout << "b2.a.b.a.b.c: " << Q::b2::a::b::a::b::c::path() << endl;
}
