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'd have to make those template aliases. I will also have to write nearly N many more aliases (where N is the size of the other enum), if I add more values.

Macros seem like a good answer at first until you try to actually write the macro generator for these and realize you're using nearly all the wild macro hacks in the book and it only gets mildly better if you decide to take on another dependency and use boost pp. I also theoretically would want to give the enumerators special values instead of just 0 through N-1, and that makes it even more complicated.

Another problem is that using macros to generate the N*M aliases I hypothetically want means I have to essentially bundle both enumerations in the macro logic, as well as the using statements. This also means I'd have to use some forward declarations to make sure that my ABC::Color class knows about the enums or that my aliases know about my Color class (depending on where I place the macro). Not the end of the world, but just more clutter.

But the real problem is that, while this appears to be a "simple" N*M permutation generator, it actually isn't. Not every permutation makes sense. Color_RGBA_332, for example, makes no sense, and that essentially means I can't use a macro generator unless I want to break up my enums and radically change my design. 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 these enums were larger or if there were more enums in play. I'm already going to have to make template specializations for these, so ideally I could just lean on those to throw a compile error for unimplemented permutations and not have to redundantly type out all of the valid permutations and hope I got them all or that I didn't make a typo along the way.

So, while I understand some of the reasons given for why we might not want to do this, hopefully now you can at least appreciate the pain and my motivation for wanting something like this in the language in some form or another. I also don't think this is as niche as you might think it is. This is not the first time I have stumbled into this mess and wanted some way out, nor am I the only person who has.

Thanks,
Ryan Klaus

On Mon, Nov 28, 2022 at 12:10 PM Arthur O'Dwyer <arthur.j.odwyer@gmail.com> wrote:
On Mon, Nov 28, 2022 at 12:50 PM Ryan Klaus <rfklaus117@gmail.com> wrote:
Yeah, I knew it would be scary, but I braved it anyway because this has been a recurring pain point for me and has often made me reach for less satisfying alternatives or ones with other draw-backs that I'm not willing to incur (macros limit the design/readability in other ways, among other problems).

I think macros are a perfect solution to your specific problem here. There may be other situations where macros would cause problems, but this doesn't seem to be one of them.
 
[...]
I'm not sure what problem your last example is trying to demonstrate though. I'm not sure if there is supposed to be an implied attribute on S? I definitely wouldn't want enumeration values to be automatically deduced without explicit say-so, which is why I proposed the "context" attribute.
Regardless, the point that Peter brought up about ignorable attributes (nice article on that, by the way!) is a good one and alone makes this a less than solid proposal barring some alternative syntax.

Ah, I had completely missed that you were proposing this as only an opt-in syntax. That makes your proposed solution
- much less risky in terms of changing existing semantics, and
- much more well-specified in terms of what semantics you're actually proposing, but
- much much more of a niche case, because now it requires the library author to use some special syntax, and if the library author knew about this problem, they'd simply not introduce the problem to begin with.

For example, a library author who foresaw this use-case and wanted to make the syntax shorter could simply do

    namespace ABC {
        using Color_RGBA_8888 = Color<ColorComponents::RGBA, ColorComponentLayout::_8888>;
    }

and then the use-case for the client becomes simply

    auto color = ABC::Color_RGBA_8888;

This works already in C++11; no need for any attributes or macros or anything. (Although macros could still be helpful to generate all of the Color_X_Y type aliases, depending on how many of them there were.)

A library author who doesn't foresee this use-case simply won't use your attribute, and then (as far as I can tell, now) you're simply proposing that there should be nothing the client can do at all (besides add their own macro or type alias).

It's also worth noting that this is an O(1) problem. The client will write
    namespace My {
        using Color_RGBA_8888 = ABC::Color<ABC::ColorComponents::RGBA, ABC::ColorComponentLayout::_8888>;
    }
in only one place in their entire codebase, so making this line a few characters shorter doesn't really buy us anything that's worth changing a whole language over.

–Arthur