Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #88554 > unrolled thread

Style question using enums

Started byPaul N <gw7rib@aol.com>
First post2023-01-17 04:29 -0800
Last post2023-01-17 22:15 -0500
Articles 9 — 6 participants

Back to article view | Back to comp.lang.c++


Contents

  Style question using enums Paul N <gw7rib@aol.com> - 2023-01-17 04:29 -0800
    Re: Style question using enums Paavo Helde <eesnimi@osa.pri.ee> - 2023-01-17 15:15 +0200
      Re: Style question using enums Paul N <gw7rib@aol.com> - 2023-01-17 05:44 -0800
    Re: Style question using enums Öö Tiib <ootiib@hot.ee> - 2023-01-17 05:23 -0800
      Re: Style question using enums Paul N <gw7rib@aol.com> - 2023-01-17 05:52 -0800
        Re: Style question using enums David Brown <david.brown@hesbynett.no> - 2023-01-17 17:00 +0100
          Re: Style question using enums Paul N <gw7rib@aol.com> - 2023-01-17 08:17 -0800
        Re: Style question using enums Bo Persson <bo@bo-persson.se> - 2023-01-17 18:43 +0100
    Re: Style question using enums Richard Damon <Richard@Damon-Family.org> - 2023-01-17 22:15 -0500

#88554 — Style question using enums

FromPaul N <gw7rib@aol.com>
Date2023-01-17 04:29 -0800
SubjectStyle question using enums
Message-ID<2872908e-bb54-428d-9118-2b5520943b34n@googlegroups.com>
I'm attempting to write a Bridge program, and it's getting quite messy.

At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows:

enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2,
	B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27,
	B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42,
	B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57,
	B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 };

This lets me do code such as

if (WentMP(B_1NT, B_2NT)) { // rebid after 1NT - 2NT
	if (pc == 14 || (pc == 13 && tens >= 2)) { mess = _TEXT("Raise");             bid = B_3NT; }
	else                                     { mess = _TEXT("Not got 25 points");           } }

which is relatively clear.

However, now that I'm trying to deal with suit bids as well it seems silly to deal with each bid separately. I have an enum

enum { clubs, diamonds, hearts, spades, notrumps };

and a function

int b(int level, int suit) { return 15 * level + 3 * suit; }

which allow me to use for instance either B_1NT or b(1, notrumps) as the value for a one notrumps bid.

Part of me thinks I ought to update the first enum to be more like

enum { B_1NT = b(1, notrumps) }

but this does not work, even if I make b inline.

Presumably if would work if I made b a macro, but these are generally discouraged. I'm guessing that some syntax using constexpr might work (I've never used one of them before) but would I need to include the code for b twice, once for the constants and once as a normal function?

All thoughts welcome!

[toc] | [next] | [standalone]


#88556

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2023-01-17 15:15 +0200
Message-ID<tq671q$389jm$1@dont-email.me>
In reply to#88554
17.01.2023 14:29 Paul N kirjutas:
> I'm attempting to write a Bridge program, and it's getting quite messy.
> 
> At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows:
> 
> enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2,
> 	B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27,
> 	B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42,
> 	B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57,
> 	B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 };
> 
> This lets me do code such as
> 
> if (WentMP(B_1NT, B_2NT)) { // rebid after 1NT - 2NT
> 	if (pc == 14 || (pc == 13 && tens >= 2)) { mess = _TEXT("Raise");             bid = B_3NT; }
> 	else                                     { mess = _TEXT("Not got 25 points");           } }
> 
> which is relatively clear.
> 
> However, now that I'm trying to deal with suit bids as well it seems silly to deal with each bid separately. I have an enum
> 
> enum { clubs, diamonds, hearts, spades, notrumps };
> 
> and a function
> 
> int b(int level, int suit) { return 15 * level + 3 * suit; }
> 
> which allow me to use for instance either B_1NT or b(1, notrumps) as the value for a one notrumps bid.
> 
> Part of me thinks I ought to update the first enum to be more like
> 
> enum { B_1NT = b(1, notrumps) }
> 
> but this does not work, even if I make b inline.

Use constexpr or consteval, depending on if you want to use b() also 
with non-compile-time arguments or not.

constexpr int b(int level, int suit) { return 15 * level + 3 * suit; }


[toc] | [prev] | [next] | [standalone]


#88558

FromPaul N <gw7rib@aol.com>
Date2023-01-17 05:44 -0800
Message-ID<30c8d028-f63e-4da4-a225-5f3d03e1d24an@googlegroups.com>
In reply to#88556
On Tuesday, January 17, 2023 at 1:15:56 PM UTC, Paavo Helde wrote:
> 17.01.2023 14:29 Paul N kirjutas: 
> > I'm attempting to write a Bridge program, and it's getting quite messy. 
> > 
> > At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows: 
> > 
> > enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2, 
> > B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27, 
> > B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42, 
> > B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57, 
> > B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 }; 
> > 
> > This lets me do code such as 
> > 
> > if (WentMP(B_1NT, B_2NT)) { // rebid after 1NT - 2NT 
> > if (pc == 14 || (pc == 13 && tens >= 2)) { mess = _TEXT("Raise"); bid = B_3NT; } 
> > else { mess = _TEXT("Not got 25 points"); } } 
> > 
> > which is relatively clear. 
> > 
> > However, now that I'm trying to deal with suit bids as well it seems silly to deal with each bid separately. I have an enum 
> > 
> > enum { clubs, diamonds, hearts, spades, notrumps }; 
> > 
> > and a function 
> > 
> > int b(int level, int suit) { return 15 * level + 3 * suit; } 
> > 
> > which allow me to use for instance either B_1NT or b(1, notrumps) as the value for a one notrumps bid. 
> > 
> > Part of me thinks I ought to update the first enum to be more like 
> > 
> > enum { B_1NT = b(1, notrumps) } 
> > 
> > but this does not work, even if I make b inline.
> Use constexpr or consteval, depending on if you want to use b() also 
> with non-compile-time arguments or not. 
> 
> constexpr int b(int level, int suit) { return 15 * level + 3 * suit; }

Thanks Paavo, that's just the job. I didn't realise constexpr could handle both compile-time and non-compile-time values.

[toc] | [prev] | [next] | [standalone]


#88557

FromÖö Tiib <ootiib@hot.ee>
Date2023-01-17 05:23 -0800
Message-ID<bee4e8a5-924a-4b71-a843-f0cc52260d1cn@googlegroups.com>
In reply to#88554
On Tuesday, 17 January 2023 at 14:29:51 UTC+2, Paul N wrote:
> I'm attempting to write a Bridge program, and it's getting quite messy. 
> 
> At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows: 
> 
> enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2, 
> B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27, 
> B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42, 
> B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57, 
> B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 }; 
> 

Nameless type, SCREAMING CAPS names and then using just ints anyway?

> This lets me do code such as 
> 
> if (WentMP(B_1NT, B_2NT)) { // rebid after 1NT - 2NT 
> if (pc == 14 || (pc == 13 && tens >= 2)) { mess = _TEXT("Raise"); bid = B_3NT; } 
> else { mess = _TEXT("Not got 25 points"); } } 
> 
> which is relatively clear. 
> 
> However, now that I'm trying to deal with suit bids as well it seems silly to deal with each bid separately. I have an enum 
> 
> enum { clubs, diamonds, hearts, spades, notrumps }; 
> 
> and a function 
> 
> int b(int level, int suit) { return 15 * level + 3 * suit; } 

Here you clearly want to have constexpr function.
 
> 
> which allow me to use for instance either B_1NT or b(1, notrumps) as the value for a one notrumps bid. 
> 
> Part of me thinks I ought to update the first enum to be more like 
> 
> enum { B_1NT = b(1, notrumps) } 
> 
> but this does not work, even if I make b inline. 
> 

You want constexpr, not inline. The constexpr implies inline.  

> Presumably if would work if I made b a macro, but these are generally discouraged. I'm guessing that some syntax using constexpr might work (I've never used one of them before) but would I need to include the code for b twice, once for the constants and once as a normal function? 

You mix up constexpr and consteval here. The consteval function must run 
compile time but constexpr can be ran run time as well.

[toc] | [prev] | [next] | [standalone]


#88559

FromPaul N <gw7rib@aol.com>
Date2023-01-17 05:52 -0800
Message-ID<9ad0862c-1f69-45a2-bba6-1f3efc518177n@googlegroups.com>
In reply to#88557
On Tuesday, January 17, 2023 at 1:23:47 PM UTC, Öö Tiib wrote:
> On Tuesday, 17 January 2023 at 14:29:51 UTC+2, Paul N wrote: 
> > I'm attempting to write a Bridge program, and it's getting quite messy. 
> > 
> > At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows: 
> > 
> > enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2, 
> > B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27, 
> > B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42, 
> > B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57, 
> > B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 }; 
> >
> Nameless type, SCREAMING CAPS names and then using just ints anyway?

Well, it's nearly a macro, isn't it? If you have any better ideas, I'd (seriously) be glad to hear them, that's the whole point of this post.

The values aren't random, they also represent contracts, with 16 representing 1 clubs doubled, 17 representing 1 clubs redoubled, etc.

> > This lets me do code such as 
> > 
> > if (WentMP(B_1NT, B_2NT)) { // rebid after 1NT - 2NT 
> > if (pc == 14 || (pc == 13 && tens >= 2)) { mess = _TEXT("Raise"); bid = B_3NT; } 
> > else { mess = _TEXT("Not got 25 points"); } } 
> > 
> > which is relatively clear. 
> > 
> > However, now that I'm trying to deal with suit bids as well it seems silly to deal with each bid separately. I have an enum 
> > 
> > enum { clubs, diamonds, hearts, spades, notrumps }; 
> > 
> > and a function 
> > 
> > int b(int level, int suit) { return 15 * level + 3 * suit; }
> Here you clearly want to have constexpr function.

Yes, Paavo said that too and gave me the syntax. I perhaps ought to keep up more with these recent (!) developments to the language.

> > which allow me to use for instance either B_1NT or b(1, notrumps) as the value for a one notrumps bid. 
> > 
> > Part of me thinks I ought to update the first enum to be more like 
> > 
> > enum { B_1NT = b(1, notrumps) } 
> > 
> > but this does not work, even if I make b inline. 
> >
> You want constexpr, not inline. The constexpr implies inline.
> > Presumably if would work if I made b a macro, but these are generally discouraged. I'm guessing that some syntax using constexpr might work (I've never used one of them before) but would I need to include the code for b twice, once for the constants and once as a normal function?
> You mix up constexpr and consteval here. The consteval function must run 
> compile time but constexpr can be ran run time as well.

Thanks for the explanation. I'd heard of constexpr (but didn't know much about it) whereas I'd not heard of consteval.

[toc] | [prev] | [next] | [standalone]


#88562

FromDavid Brown <david.brown@hesbynett.no>
Date2023-01-17 17:00 +0100
Message-ID<tq6gml$39uct$1@dont-email.me>
In reply to#88559
On 17/01/2023 14:52, Paul N wrote:
> On Tuesday, January 17, 2023 at 1:23:47 PM UTC, Öö Tiib wrote:
>> On Tuesday, 17 January 2023 at 14:29:51 UTC+2, Paul N wrote:
>>> I'm attempting to write a Bridge program, and it's getting quite messy.
>>>
>>> At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows:
>>>
>>> enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2,
>>> B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27,
>>> B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42,
>>> B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57,
>>> B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 };
>>>
>> Nameless type, SCREAMING CAPS names and then using just ints anyway?
> 
> Well, it's nearly a macro, isn't it? If you have any better ideas,
> I'd (seriously) be glad to hear them, that's the whole point of this
> post.
> 

Macro names don't need all caps.  I only ever use all-caps names if I 
have a macro that is doing something weird, and you have to pay special 
attention to it.  So if I have a function-like macro that behaves very 
much like a function, it does not need all-caps in the name.  (Of 
course, in C++ this generally would be a real function - it's more 
common in C, when you need something that is type generic and you don't 
have templates.)  Similarly, macros that are simple constants do not 
need all-caps names.  (And again, there are usually better choices than 
macros.)

If the macro evaluates a parameter more than once, or plays silly 
buggers with scopes, declares new variables, etc., in a way that is not 
obvious in its use, then all-caps is a good warning.  But overuse of a 
warning negates its effectiveness.


> The values aren't random, they also represent contracts, with 16
> representing 1 clubs doubled, 17 representing 1 clubs redoubled,
> etc.
> 

Then don't define them this way.  Do it using a constexpr function, as 
discussed in other posts, or by some completely different arrangement 
(such as a const template variable, or a class that wraps the value 
while storing it in a bitfield struct - there are a number of options). 
Whenever there are hand-calculated magic numbers, you should be 
sceptical.  It /might/ be the simplest and clearest way to write the 
code, but it might not be.  And it's seldom the most /fun/ way to write 
the code!

[toc] | [prev] | [next] | [standalone]


#88563

FromPaul N <gw7rib@aol.com>
Date2023-01-17 08:17 -0800
Message-ID<768d824e-ea48-4497-ade6-171afdf98425n@googlegroups.com>
In reply to#88562
On Tuesday, January 17, 2023 at 4:00:38 PM UTC, David Brown wrote:
> On 17/01/2023 14:52, Paul N wrote: 
> > On Tuesday, January 17, 2023 at 1:23:47 PM UTC, Öö Tiib wrote: 
> >> On Tuesday, 17 January 2023 at 14:29:51 UTC+2, Paul N wrote: 
> >>> I'm attempting to write a Bridge program, and it's getting quite messy. 
> >>> 
> >>> At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows: 
> >>> 
> >>> enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2, 
> >>> B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27, 
> >>> B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42, 
> >>> B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57, 
> >>> B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 }; 
> >>> 
> >> Nameless type, SCREAMING CAPS names and then using just ints anyway? 
> > 
> > Well, it's nearly a macro, isn't it? If you have any better ideas, 
> > I'd (seriously) be glad to hear them, that's the whole point of this 
> > post. 
> >
> Macro names don't need all caps. I only ever use all-caps names if I 
> have a macro that is doing something weird, and you have to pay special 
> attention to it. So if I have a function-like macro that behaves very 
> much like a function, it does not need all-caps in the name. (Of 
> course, in C++ this generally would be a real function - it's more 
> common in C, when you need something that is type generic and you don't 
> have templates.) Similarly, macros that are simple constants do not 
> need all-caps names. (And again, there are usually better choices than 
> macros.) 
> 
> If the macro evaluates a parameter more than once, or plays silly 
> buggers with scopes, declares new variables, etc., in a way that is not 
> obvious in its use, then all-caps is a good warning. But overuse of a 
> warning negates its effectiveness.
> > The values aren't random, they also represent contracts, with 16 
> > representing 1 clubs doubled, 17 representing 1 clubs redoubled, 
> > etc. 
> >
> Then don't define them this way. Do it using a constexpr function, as 
> discussed in other posts, or by some completely different arrangement 
> (such as a const template variable, or a class that wraps the value 
> while storing it in a bitfield struct - there are a number of options). 
> Whenever there are hand-calculated magic numbers, you should be 
> sceptical. It /might/ be the simplest and clearest way to write the 
> code, but it might not be. And it's seldom the most /fun/ way to write 
> the code!

Thanks for the comments, David. My first attempt at the code used all the magic numbers and so having an enum was at least an improvement on that. Now that I have the function, I have more options, but it's still not entirely clear to me whether changing code from the already written bid = B_3NT; to the slightly longer bid  = b(3, notrumps) is really an improvement. Hence the canvassing of opinions.

[toc] | [prev] | [next] | [standalone]


#88572

FromBo Persson <bo@bo-persson.se>
Date2023-01-17 18:43 +0100
Message-ID<k2o519Fag27U1@mid.individual.net>
In reply to#88559
On 2023-01-17 at 14:52, Paul N wrote:
> On Tuesday, January 17, 2023 at 1:23:47 PM UTC, Öö Tiib wrote:
>> On Tuesday, 17 January 2023 at 14:29:51 UTC+2, Paul N wrote:
>>>
>>> and a function
>>>
>>> int b(int level, int suit) { return 15 * level + 3 * suit; }
 >>
>> Here you clearly want to have constexpr function.
> 
> Yes, Paavo said that too and gave me the syntax. I perhaps ought to keep up more with these recent (!) developments to the language.
> 


Yes, as recent as 2011.  :-)

[toc] | [prev] | [next] | [standalone]


#88585

FromRichard Damon <Richard@Damon-Family.org>
Date2023-01-17 22:15 -0500
Message-ID<0vJxL.340435$iU59.335379@fx14.iad>
In reply to#88554
On 1/17/23 7:29 AM, Paul N wrote:
> I'm attempting to write a Bridge program, and it's getting quite messy.
> 
> At first I simply used a number for each bid, so my code had a lot of "magic numbers" in it. I got rid of some by using an enum, as follows:
> 
> enum { B_PASS = 0, B_DOUBLE = 1, B_REDOUBLE = 2,
> 	B_1C = 15, B_1D = 18, B_1H = 21, B_1S = 24, B_1NT = 27,
> 	B_2C = 30, B_2D = 33, B_2H = 36, B_2S = 39, B_2NT = 42,
> 	B_3C = 45, B_3D = 48, B_3H = 51, B_3S = 54, B_3NT = 57,
> 	B_4C = 60, B_4D = 63, B_4H = 66, B_4S = 69, B_4NT = 72 };
> 
> This lets me do code such as
> 
> if (WentMP(B_1NT, B_2NT)) { // rebid after 1NT - 2NT
> 	if (pc == 14 || (pc == 13 && tens >= 2)) { mess = _TEXT("Raise");             bid = B_3NT; }
> 	else                                     { mess = _TEXT("Not got 25 points");           } }
> 
> which is relatively clear.
> 
> However, now that I'm trying to deal with suit bids as well it seems silly to deal with each bid separately. I have an enum
> 
> enum { clubs, diamonds, hearts, spades, notrumps };
> 
> and a function
> 
> int b(int level, int suit) { return 15 * level + 3 * suit; }
> 
> which allow me to use for instance either B_1NT or b(1, notrumps) as the value for a one notrumps bid.
> 
> Part of me thinks I ought to update the first enum to be more like
> 
> enum { B_1NT = b(1, notrumps) }
> 
> but this does not work, even if I make b inline.
> 
> Presumably if would work if I made b a macro, but these are generally discouraged. I'm guessing that some syntax using constexpr might work (I've never used one of them before) but would I need to include the code for b twice, once for the constants and once as a normal function?
> 
> All thoughts welcome!
> 
> 

I might change the encoding of the enum, thinking of it a bit like a 
bitfield.

Bottom 2 bits is the "Double" status: 01 Undoubled, 10 Doubled, 11 
Redoubled (this lets all zeros be the "pass" option"

Next 3 bits are the suit 000 = Clubs, 001 = Diamonds, 010 = Hearts, 011 
= Spades, 100 = No Trump

Top 3 bits are the contract:  001 = 1 bid .... 111 = 7 bid.

So actual bits go from 1 Clubs Undoubled = 001 000 01 = 33 decimal to
7 NT Redoubled = 111 100 11 = 243

Perhaps even have 3 seperate enums for each of these and since you are 
using C++ define a constexpr operator that lets you merge them together. 
(you will need another enum type for Contract + Suit to build the full 
bids up.)

You can also define conversion functions to get you back to the 
components (which just need to do bit masking).

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web