Stateful Metaprogramming

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.

1
2
3
4
5
6
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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 class flag_setter has not been instantiated. We got false.
  • After the first invocation, the template class flag_setter has been instantiated. The function flag() is defined. We got true.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);
}

2 References:

  1. Is stateful metaprogramming ill-formed (yet)?
  2. Revisiting Stateful Metaprogramming in C++20
  3. Compiler Explorer

Related Content