Skip to content

Instantly share code, notes, and snippets.

@Floweynt
Created July 6, 2022 02:11
Show Gist options
  • Save Floweynt/f2dc6e510dd15145bd1f6bf9c0472490 to your computer and use it in GitHub Desktop.
Save Floweynt/f2dc6e510dd15145bd1f6bf9c0472490 to your computer and use it in GitHub Desktop.
C++ compile time config
#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