59

One of my most beloved/evil inventions I've had the fortune to come across is the constexpr counter, aka stateful metaprogramming. As mentioned in the post, it seems to be legal under C++14, and I'm wondering has anything changed with C++17?

The following is an implementation largely based on the post

template <int N>
struct flag
{
    friend constexpr int adl_flag(flag<N>);
    constexpr operator int() { return N; }
};

template <int N>
struct write
{
    friend constexpr int adl_flag(flag<N>) { return N; }
    static constexpr int value = N;
};

template <int N, int = adl_flag(flag<N>{})>
constexpr int read(int, flag<N>, int R = read(0, flag<N + 1>{}))
{
    return R;
}

template <int N>
constexpr int read(float, flag<N>)
{
    return N;
}

template <int N = 0>
constexpr int counter(int R = write<read(0, flag<0>{}) + N>::value)
{
    return R;
}

And we use it as

static_assert(counter() != counter(), "Your compiler is mad at you"); 

template<int = counter()>
struct S {};

static_assert(!std::is_same_v<S<>, S<>>, "This is ridiculous");

This by the way, is a direct contradiction to Storing States in C++ Metaprogramming?

Barry
  • 286,269
  • 29
  • 621
  • 977
Passer By
  • 19,325
  • 6
  • 49
  • 96
  • How does `read(0, flag{})` not result in an infinite loop? The literal 0 causes it to call the first overload (`int` being preferred over `float`), which will naturally call it again and again and again. What is the terminating condition? – Nicol Bolas May 30 '17 at 16:56
  • 1
    @NicolBolas By SFINAE, the `int` overload of `read(0, flag{})` cannot be called for some large enough `N` since we have not yet defined `adl_flag(flag)`, therefore the `float` overload would be called. For the full explanation, the linked post is written excellently. – Passer By May 30 '17 at 16:58
  • 2
    Note also the flaws pointed out by David Krauss in the corresponding [std-discussion](https://groups.google.com/a/isocpp.org/d/msg/std-discussion/M6aJMH_ewoM/eAaBooYw-MsJ) thread, which Filip promised to address in a fourth post but never did. – Columbo May 30 '17 at 17:30
  • 11
    This is awful, I love it! – Mark K Cowan May 30 '17 at 23:50
  • Is the compiler actually required to re-evaluate a default template parameter every time the template is used (without specifying that parameter)? – feersum May 31 '17 at 16:24
  • @Columbo there are many reasons for not continuing that blog series, among them pressure from people active within the working group, I have addressed the "issues" elsewhere (sadly these are not made public (yet)). – Filip Roséen - refp Mar 31 '18 at 14:08
  • @FilipRoséen-refp "among them pressure from within the working group" What? That's completely ludicrous.. – Columbo Mar 31 '18 at 14:10
  • @Columbo I can ask the relevant parties for the permission to share what they sent me, as these came per direct emails from them (plural) to me. Also, see updated comment for clearification. – Filip Roséen - refp Mar 31 '18 at 14:12
  • @FilipRoséen-refp I am a member of the ISO group (UK panel). I'm sure it's fine.. – Columbo Mar 31 '18 at 14:13
  • @Columbo I do not disclose emails where I am the sole recipient to the public without explicit permission from the author, ever. – Filip Roséen - refp Mar 31 '18 at 14:14
  • @FilipRoséen-refp On the other hand, we're not working in the pentagon... ;-) – Columbo Mar 31 '18 at 14:14
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/167942/discussion-between-filip-roseen-refp-and-columbo). – Filip Roséen - refp Mar 31 '18 at 14:14
  • 1
    https://godbolt.org/z/GafqTT5vh gcc x86-64 12.2 does not compile the same code Neither does Clang 15.0.0 – Sergey Kolesnik Jan 27 '23 at 16:07

1 Answers1

46

This is CWG active issue 2118:

Defining a friend function in a template, then referencing that function later provides a means of capturing and retrieving metaprogramming state. This technique is arcane and should be made ill-formed.

Notes from the May, 2015 meeting:

CWG agreed that such techniques should be ill-formed, although the mechanism for prohibiting them is as yet undetermined.

It's still an active issue, nothing will change in C++17 at least for now. Though when such a prohibition mechanism is determined, this may be retroactively ruled as a DR.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 12
    Does "this technique is arcane (_i.e. understood by a few, mysterious, secret_)" really mean that it "should be made ill-formed"? Many of the _standard_ c++ idioms we have today essentially started that way (TMP, CRTP, ADL to name a few). – Andy G May 11 '19 at 08:36
  • Does this mean any use of defining a friend function in a template and referencing it later will be prohibited even when its not used for capturing metaprogramming state? This can be used to add associated classes to ADL, like [here](https://stackoverflow.com/q/71230435/375343). This doesn't seem to be stateful metaprogramming although it relies on the same mechanism. – Paul Fultz II Feb 23 '22 at 23:08
  • @PaulFultzII I think that more or less exactly matches the issue description? Otherwise, I don't know any more than what the issue says. Hasn't been any movement on this in the last... getting close to 5 years that I'm aware of. – Barry Feb 23 '22 at 23:17
  • @AndyG: This one is different because it breaks the language model—consider how meaningless the ODR is if different definitions of a template see different values from such a counter. – Davis Herring Feb 24 '22 at 03:38
  • 2
    @DavisHerring: I will be interested to see how they will make this ill-formed, however. There are better and more "supported" mechanisms for performing the stateful metaprogramming than what is used in the original question, mechanisms that make use of C++20 features. Compilers would actually have to detect bad usage and specifically block that usage while letting the usage considered acceptable through. I'm not sure that is possible! Better is that someone creates a boost library that uses these stateful metaprogramming features, then it'll become a first-class feature of the language! – Andy G Mar 01 '22 at 16:19
  • Can you post an update on the issue? Latest compilers (gcc 12.2 && clang 15.0.0) does not compile the code from the question https://godbolt.org/z/GafqTT5vh – Sergey Kolesnik Jan 27 '23 at 16:09
  • @SergeyKolesnik There's not really an update to post. – Barry Jan 27 '23 at 16:21
  • @AndyG I suspect that whoever wrote the issue description didn't mean "we should ban this because C++ should not support arcane idioms", but "we *can* ban this because it's not widely used". As for why CWG actually wants to ban it, I guess what Davis Herring mentioned might be one reason. – Brian Bi May 15 '23 at 20:05