Created
July 6, 2022 02:11
-
-
Save Floweynt/f2dc6e510dd15145bd1f6bf9c0472490 to your computer and use it in GitHub Desktop.
C++ compile time config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <concepts> | |
#include <type_traits> | |
#include <algorithm> | |
#include <string_view> | |
namespace detail | |
{ | |
template<typename T> constexpr bool always_false = false; | |
template<typename T> | |
std::add_rvalue_reference_t<T> _declval() { exit(-1); } | |
template<std::size_t N> | |
struct string_literal | |
{ | |
constexpr string_literal(const char (&str)[N]) | |
{ | |
std::copy_n(str, N, value); | |
} | |
constexpr string_literal(const char (&str)[N - 1], char ch) | |
{ | |
std::copy_n(str, N - 2, value); | |
value[N - 2] = ch; | |
value[N - 1] = 0; | |
} | |
char value[N]; | |
}; | |
template<typename T> | |
struct is_strlit : std::false_type {}; | |
template<std::size_t N> | |
struct is_strlit<string_literal<N>> : std::true_type {}; | |
template<typename T> | |
concept strlit = is_strlit<std::decay_t<T>>::value; | |
template<typename... T> | |
struct type_list { }; | |
template<typename... T1, typename... T2> | |
type_list<T1..., T2...> operator+(type_list<T1...>, type_list<T2...>) | |
{ | |
return _declval<type_list<T1..., T2...>>(); | |
} | |
struct error { }; | |
template<typename T, T v> | |
struct comptime_value | |
{ | |
using type = T; | |
inline static constexpr auto value = v; | |
}; | |
template<typename T> | |
concept config_entry_concept = requires(T a) | |
{ | |
{ T::name } -> strlit<>; | |
std::is_class_v<typename T::type>; | |
}; | |
template<string_literal v, config_entry_concept Opt> | |
using check = std::conditional_t< | |
std::string_view(v.value) == std::string_view(Opt::name.value), | |
type_list<Opt>, | |
type_list<> | |
>; | |
template<typename T, typename... Ts> | |
struct extract_first { using type = T; }; | |
template<string_literal v, config_entry_concept... Opts> | |
using check_all = decltype((type_list<>{} + ... + check<v, Opts>{})); | |
template<string_literal l, config_entry_concept... Opts> | |
using search_result_t = std::decay_t<decltype([]<typename... Ts>(type_list<Ts...> ts) { | |
if constexpr(sizeof...(Ts) == 0) | |
return error{}; | |
else | |
return _declval<typename extract_first<Ts...>::type>(); | |
}(_declval<check_all<l, Opts...>>()))>; | |
template<string_literal... S> | |
struct string_list_container { }; | |
template<string_literal... S, char ch> | |
auto operator+(string_list_container<S...> container, comptime_value<char, ch> app) | |
{ | |
if constexpr(ch == '.') | |
return string_list_container<"", S...>{}; | |
else | |
return []<string_literal First, string_literal... Rest>(string_list_container<First, Rest...>) { | |
return string_list_container<string_literal<sizeof(First.value) + 1>(First.value, ch), Rest...>{}; | |
}(container); | |
} | |
template<string_literal l, string_literal... S> | |
auto operator+(string_list_container<S...>, comptime_value<decltype(l), l>) | |
{ | |
return string_list_container<l, S...>{}; | |
} | |
template<string_literal l> | |
using make_comptime_strlit = comptime_value<decltype(l), l>; | |
template<char...> | |
struct char_list {}; | |
template<std::size_t i, string_literal l> | |
auto to_char_list() | |
{ | |
if constexpr (i + 1 == sizeof(l.value)) | |
return char_list<>{}; | |
else | |
return []<char... Chs>(char_list<Chs...>) { | |
return char_list<l.value[i], Chs...>{}; | |
}(to_char_list<i + 1, l>()); | |
} | |
template<string_literal S> | |
auto make_path_helper() | |
{ | |
using chars = decltype(to_char_list<0, S>()); | |
using reversed_strs = decltype([]<char... Chs>(char_list<Chs...>) { | |
return (string_list_container<"">{} + ... + comptime_value<char, Chs>{}); | |
}(chars{})); | |
using path_parts = decltype([]<string_literal... Strs>(string_list_container<Strs...>){ | |
return (string_list_container<>{} + ... + comptime_value<decltype(Strs), Strs>{}); | |
}(reversed_strs{})); | |
return path_parts{}; | |
} | |
template<string_literal S> | |
using make_path = decltype(make_path_helper<S>()); | |
template<typename T, string_literal S> | |
concept config_holder_concept = requires(T a) | |
{ | |
std::is_class_v<typename T::template get<S>>; | |
T::template exists<S>; | |
}; | |
template<typename C, string_literal N> | |
auto operator+(C, comptime_value<decltype(N), N>) | |
{ | |
if constexpr(!config_holder_concept<C, N>) | |
return error{}; | |
else if constexpr(!std::is_default_constructible_v<typename C::template get<N>>) | |
return error{}; | |
else | |
return _declval<typename C::template get<N>>(); | |
} | |
template<string_literal N> | |
auto operator+(error, comptime_value<decltype(N), N>) | |
{ | |
return error{}; | |
} | |
template<typename R, string_literal S> | |
using get_path = decltype([]<string_literal... Parts>(string_list_container<Parts...>){ | |
return (_declval<R>() + ... + comptime_value<decltype(Parts), Parts>{}); | |
}(make_path<S>{})); | |
} | |
template<typename T, T v> | |
using simple_data_holder = detail::comptime_value<T, v>; | |
template<detail::string_literal n, typename T> | |
struct config_entry | |
{ | |
inline static constexpr auto name = n; | |
using type = T; | |
}; | |
template<detail::config_entry_concept... Opts> | |
struct config_holder | |
{ | |
template<detail::string_literal str> | |
requires(!std::is_same_v<detail::error, typename detail::search_result_t<str, Opts...>>) | |
using get = typename detail::search_result_t<str, Opts...>::type; | |
template<detail::string_literal str> | |
requires(!std::is_same_v<detail::error, typename detail::search_result_t<str, Opts...>>) | |
inline static constexpr auto get_val = get<str>::value; | |
template<detail::string_literal str> | |
requires(!std::is_same_v<detail::error, typename detail::get_path<config_holder, str>>) | |
using get_path = typename detail::get_path<config_holder, str>; | |
template<detail::string_literal str> | |
requires(!std::is_same_v<detail::error, typename detail::get_path<config_holder, str>>) | |
inline static constexpr auto get_path_val = get_path<str>::value; | |
template<detail::string_literal str> | |
inline static constexpr auto exists = !std::is_same_v<detail::error, typename detail::search_result_t<str, Opts...>>; | |
}; | |
template<detail::string_literal n, typename T, T v> | |
using simple_entry = config_entry<n, simple_data_holder<T, v>>; | |
template<detail::string_literal n, detail::config_entry_concept... Opts> | |
using nested_entry = config_entry<n, config_holder<Opts...>>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment