On Tue, Nov 29, 2022 at 11:48 AM Ryan Klaus <rfklaus117@gmail.com> wrote:
Yeah, type aliases are what I always end up going with, it's just exceptionally non-ideal, especially if I decide to add more parameters. And if I need/want some parameters to be optional or I want to add a type that could be user defined [...] I also theoretically would want to give the enumerators special values instead of just 0 through N-1 [...] Not every permutation makes sense. Color_RGBA_332, for example, makes no sense, and that essentially means I can't use a macro generator [...] These aren't incredibly large enums, so writing out the valid permutations by hand is fine, but it's not hard to see how this could become a very suboptimal/error-prone experience if [...]

This whole paragraph seems to me like a great argument for why not to modify the language for this specific use-case. There's so many different ways you might change your design parameters, that ultimately your solution is going to end up tightly coupled to whatever your design parameters are today — and not what they might be tomorrow, or what someone else's design parameters might be...
This is the same kind of pickle we're in with type erasure; see https://quuxplusone.github.io/blog/2019/03/27/design-space-for-std-function/ for all the design knobs there, which is why I always say our job should be to teach the techniques, not to try to standardize any one specific set of design choices that will by definition not satisfy the majority of people today, or anybody at all tomorrow.
And again, remember this is an O(1) problem — the library writer just writes O(1) type aliases and is done forever. (As you point out, it's not even O(n*m) type aliases, because you only need the aliases that make logical sense! Actually this is a great reason not to even expose (n, m) enumeration values in the first place. Expose one enumerator per format that makes sense, and don't even allow your client to express formats that don't make sense. If `ABC::Color<RGBA, _332>` is expressible, it should be meaningful; and if it's not meaningful, then it shouldn't be expressible.)

Having said that the whole thing is a bad idea, though, I'll piggyback on Lénárd's idea and scope-creep it a little further: Maybe what you're looking for is a way to introduce new name-lookup scopes in the middle of an expression-in-progress. For example,

    namespace N {
        static constexpr int A = 42;
        void f(int x);
        template<int> void g();
        struct S { int x; };
        template<int> struct T {};
    }
    int main() {
        N::f(A);  // error today, but maybe you want to let N::f "opt in" to namespace N for the parsing of its function parameter(s)
        N::g<A>();  // error today, but maybe you want to let N::g "opt in" to namespace N for the parsing of its template parameter(s)
        N::S s1 = {A};  // error today, but maybe you want to let N::S "opt in" to namespace N for the parsing of its initializer(s)
        auto s2 = N::S(A);  // error today, but maybe you want to let N::S "opt in" to namespace N for the parsing of its initializer(s)
        auto t = N::T<A>();  // error today, but maybe you want to let N::T "opt in" to namespace N for the parsing of its template parameter(s)
    }

I believe the `N::T` example is basically the same as what you're talking about (and just as achievable or unachievable, wording-wise, which is to say, it goes against every principle of C++ parser design).
So the question is, if you're allowing `N::T`, should you look for a syntax that also allows all these other permutations?

Now, maybe I'm just moving the goalposts here from the moon to the stars; you might think "C'mon, man, I might have a chance to go to the moon, but no way can I make it to the stars!" Possible. On the other hand, I really don't think you have any chance of making it to the moon, so why not try for the stars? :P

–Arthur,
misusing proverbs