Stateful Metaprogramming
Contents
1 Stateful Metaprogramming
A C++ trick is known as “stateful metaprogramming”, in which one manipulates friend functions and template instantiation to introduce a utilizable state into the compilation.
constexpr bool func() { /* something */ }
constexpr bool a = func();
constexpr bool b = func();
// Think this assertion always fails? With stateful metaprogramming, think again.
static_assert(a != b);
Maybe ill-formed? or UB?
1.1 Explicit Template Instantiation
auto flag(auto);
template<bool val>
struct flag_setter {
friend auto flag(auto) {}
};
int main() {
flag(1); // 1, error
flag_setter<true>{};
flag(1); // 2, ok
}
During the template class flag_setter
instantiation, a template class
specialization has been added to the global scope. The specialization defines
the function flag()
(by friend function). So the second invocation success.
1.2 Constant Expression Switch
auto flag(auto);
template<bool val>
struct flag_setter {
friend auto flag(auto) {
}
bool value = false;
};
template<auto arg = 0, auto condition = requires { flag(arg); }>
consteval auto value() {
if constexpr (!condition) {
return flag_setter<condition> {}.value;
}
return condition;
}
int main() {
constexpr auto a = value(); // 1: false
constexpr auto b = value(); // 2: true
static_assert(a != b);
}
- At
// 1
the template classflag_setter
has not been instantiated. We gotfalse
. - After the first invocation, the template class
flag_setter
has been instantiated. The functionflag()
is defined. We gottrue
.
1.3 Compile-time Counting
template<std::size_t N>
struct reader
{
friend auto counted_flag(reader<N>);
};
template<std::size_t N>
struct setter
{
friend auto counted_flag(reader<N>) {}
std::size_t value = N;
};
template<auto N = 0, auto tag = []{}, bool condition = requires(reader<N> red){ counted_flag(red); }>
constexpr auto next()
{
if constexpr (!condition)
{
constexpr setter<N> s;
return s.value;
}
else
{
return next<N + 1>();
}
}
int main()
{
constexpr auto a = next();
constexpr auto b = next();
constexpr auto c = next();
static_assert(a == 0 && b == 1 && c == 2);
}