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;
}