Is stateful metaprogramming ill-formed (yet)?
Asked Answered
P

1

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?

Phytohormone answered 30/5, 2017 at 16:40 Comment(13)
How does read(0, flag<N + 1>{}) 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?Nipper
@NicolBolas By SFINAE, the int overload of read(0, flag<N + 1>{}) cannot be called for some large enough N since we have not yet defined adl_flag(flag<N + 1>), therefore the float overload would be called. For the full explanation, the linked post is written excellently.Phytohormone
Note also the flaws pointed out by David Krauss in the corresponding std-discussion thread, which Filip promised to address in a fourth post but never did.Interlace
This is awful, I love it!Alkalize
Is the compiler actually required to re-evaluate a default template parameter every time the template is used (without specifying that parameter)?Calabresi
@Interlace 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)).Propagandize
@FilipRoséen-refp "among them pressure from within the working group" What? That's completely ludicrous..Interlace
@Interlace 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.Propagandize
@FilipRoséen-refp I am a member of the ISO group (UK panel). I'm sure it's fine..Interlace
@Interlace I do not disclose emails where I am the sole recipient to the public without explicit permission from the author, ever.Propagandize
@FilipRoséen-refp On the other hand, we're not working in the pentagon... ;-)Interlace
Let us continue this discussion in chat.Propagandize
godbolt.org/z/GafqTT5vh gcc x86-64 12.2 does not compile the same code Neither does Clang 15.0.0Bettor
L
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.

Lannie answered 30/5, 2017 at 17:13 Comment(8)
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).Diagnostics
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. This doesn't seem to be stateful metaprogramming although it relies on the same mechanism.Aero
@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.Lannie
@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.Permission
@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!Diagnostics
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 godbolt.org/z/GafqTT5vhBettor
@SergeyKolesnik There's not really an update to post.Lannie
@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.Montpelier

© 2022 - 2024 — McMap. All rights reserved.