Reference:
https://mc-deltat.github.io/articles/stateful-metaprogramming-cpp20
Below code demonstrates even though the being generated at compile time the result can be different due to during the compile time state changes.
// declare of flag function, the function body doesn't exist
// unless template type setter being instantiated.
auto flag(int);
template<bool B> requires (!B)
struct setter {
// 'flag' definition
friend auto flag(int) {}
static constexpr bool b = B;
};
// declare [[nodiscard]] and consteval make sure the
// template function won't be elided by compiler.
template<bool FlagVal>
[[nodiscard]]
consteval auto nonconstant_constant_impl() {
if constexpr (FlagVal) {
return true;
}
else {
// 'setter' being instantiated.
setter<FlagVal> s;
return s.b;
}
}
template<
auto Arg = 0,
bool FlagVal = requires { flag(Arg); },
auto Val = nonconstant_constant_impl<FlagVal>()
>
constexpr auto nonconstant_constant = Val;
auto main() ->int{
// a = 0
// First evaluation in this TU; triggers 'setter' being
// instantiated.
constexpr bool a = nonconstant_constant<>;
// b = 1
constexpr bool b = nonconstant_constant<>;
// assertion passes.
static_assert(a != b);
}
template<unsigned N>
struct reader {
friend auto counted_flag(reader<N>);
};
template<unsigned N>
struct setter {
friend auto counted_flag(reader<N>) {}
static constexpr unsigned n = N;
};
template<
auto Tag,
unsigned NextVal = 0
>
[[nodiscard]]
consteval auto counter_impl() {
constexpr bool counted_past_value = requires(reader<NextVal> r) {
counted_flag(r);
};
if constexpr (counted_past_value) {
return counter_impl<Tag, NextVal + 1>();
}
else {
setter<NextVal> s;
return s.n;
}
}
template<
auto Tag = []{}, // Each call generates different type.
auto Val = counter_impl<Tag>()
>
constexpr auto counter = Val;
int main() {
static_assert(counter<> == 0);
static_assert(counter<> == 1);
static_assert(counter<> == 2);
static_assert(counter<> == 3);
static_assert(counter<> == 4);
static_assert(counter<> == 5);
static_assert(counter<> == 6);
static_assert(counter<> == 7);
static_assert(counter<> == 8);
static_assert(counter<> == 9);
static_assert(counter<> == 10);
}
s.t I've played around back in 2013
#include <concepts>
#include <type_traits>
template<typename...>
struct type_list {};
template<class TypeList, typename T>
struct type_list_append;
template<typename... Ts, typename T>
struct type_list_append<type_list<Ts...>, T> {
using type = type_list<Ts..., T>;
};
template<unsigned N, typename List>
struct state_t {
static constexpr unsigned n = N;
using list = List;
};
namespace {
// used in reader; thus the 'state_func' shall be unique in each TU
// thus not violating the ODR
struct tu_tag {};
}
template<
unsigned N,
std::same_as<tu_tag> TUTag
>
struct reader {
friend auto state_func(reader<N, TUTag>);
};
template<
unsigned N,
typename List,
// Preventing accidentally passing random type thus violating ODR
// It must be anonymous type 'tu_tag'
std::same_as<tu_tag> TUTag
>
struct setter {
// generated 'state_func' will be unique in each TU thanks to 'TUTag'
friend auto state_func(reader<N, TUTag>) {
return List{};
}
static constexpr state_t<N, List> state{};
};
template struct setter<0, type_list<>, tu_tag>;
template<
// Preventing accidentally passing random type thus violating ODR
// It must be anonymous type 'tu_tag'
std::same_as<tu_tag> TUTag,
auto EvalTag,
unsigned N = 0
>
[[nodiscard]]
consteval auto get_state() {
constexpr bool counted_past_n = requires(reader<N, TUTag> r) {
state_func(r);
};
if constexpr (counted_past_n) {
return get_state<TUTag, EvalTag, N + 1>();
} else {
constexpr reader<N - 1, TUTag> r;
return state_t<N - 1, decltype(state_func(r))>{};
}
}
template<
// std::same_as is a concept; taking 2 arguments. <...> binds to back paramters while
// assigned type is bound to forefront paramter.
// Use to prevent accidentally passing random type thus violating ODR
// It must be anonymous type 'tu_tag'
std::same_as<tu_tag> TUTag = tu_tag,
auto EvalTag = []{},
auto State = get_state<TUTag, EvalTag>()
>
using get_list = typename std::remove_cvref_t<decltype(State)>::list;
template<
typename T,
// Preventing accidentally passing random type thus violating ODR
// It must be anonymous type 'tu_tag'
std::same_as<tu_tag> TUTag,
auto EvalTag
>
[[nodiscard]]
consteval auto append_impl() {
using cur_state = decltype(get_state<TUTag, EvalTag>());
using cur_list = typename cur_state::list;
using new_list = typename type_list_append<cur_list, T>::type;
setter<cur_state::n + 1, new_list, TUTag> s;
return s.state;
}
template<
typename T,
// Preventing accidentally passing random type thus violating ODR
// It must be anonmous type 'tu_tag'
std::same_as<tu_tag> TUTag = tu_tag,
auto EvalTag = []{},
auto State = append_impl<T, TUTag, EvalTag>()
>
constexpr auto append = [] { return State; };
int main() {
static_assert(std::same_as<get_list<>, type_list<>>);
append<int>();
static_assert(std::same_as<get_list<>, type_list<int>>);
append<float>();
static_assert(std::same_as<get_list<>, type_list<int, float>>);
append<char>();
static_assert(std::same_as<get_list<>, type_list<int, float, char>>);
}
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.