I'm not very happy with the do {} while() statement in C

Discussion of chess software programming and technical issues.

Moderators: hgm, Harvey Williamson, bob

Forum rules
This textbox is used to restore diagrams posted with the [d] tag before the upgrade.
Post Reply
bob
Posts: 20346
Joined: Mon Feb 27, 2006 6:30 pm
Location: Birmingham, AL

Re: I'm not very happy with the do {} while() statement in C

Post by bob » Tue Feb 20, 2018 5:22 am

syzygy wrote:
Ras wrote:Goto is a perfectly fine tool - and like any tool, it can be misused. The goto aversion stems from a Dijkstra article in the 1960s, but that referred to the goto in Basic. That was another beast because it was using fixed line numbers as jump targets, which quickly could become a maintenance nightmare once you inserted additional instructions somewhere.
I fully agree that Dijkstra's article has to be understood against the background of the coding practices of that time (1968). But he was probably not thinking very much about BASIC (which is from 1964 and was probably hardly used for serious programming).
Goto usually should not be used in regular control flow, and it should only be forward goto. For error handling in C, goto is much nicer than nested ifs. As soon as you have more than one thing to clean up, nested ifs also entail code duplication. That's why goto is used in the Linux kernel.
http://koblents.com/Ches/Links/Month-Ma ... rnel-Code/

(The guy who started that thread also suggested that the Linux code was of questionable quality given the many patches coming in.)
Goto in basic. Goto in fortran. goto in Algol. Goto in PL/1. Goto even in the old RPG language. Goto in most every language I have ever used. All can be used to write clean code. And all can be misused to write code that is very hard to follow. Much of my programming over the years has been in various assembly languages where the goto (jump/branch/whatever) instruction is ALL you get for any sort of control structures. I have seen marvelously clean assembly code. I have seen marvelously convoluted assembly code. All depends on the programmer's skills...

This thread is an example of poor programming that leads to convoluted code due to pedantic reasons such as objecting to the use of a goto (break) which makes the code much easier to read than the ugly do/white syntax proposed...

The people that developed these languages (Thompson, Kernighan, Ritchie for C) were pretty bright. While C is far from perfect, the do/while is not a good example of something that was done poorly.

Michael Sherwin
Posts: 2799
Joined: Fri May 26, 2006 1:00 am
Location: OH, USA

Re: I'm not very happy with the do {} while() statement in C

Post by Michael Sherwin » Tue Feb 20, 2018 5:39 am

This is what I ended up with. I hope that I do not hurt Jumbo's feelings because I did not build up the final macros using simple macros but I did use the modulo operator as it has a counterpart in the other macro's '&'. Also there was a peanut shell with a bad peanut in it, :(. The modified do loop reintroduced the problem of double execution of SQUARE64(ts120) before entering and on entering the do loop. If that is the case then a while loop should just be used. That is unless the compiler is smart enough to jump over the second conversion in the do loop because SQUARE64(ts120) was called with the same parameter. But, I do not know if that is the case. :( But all in all I am happy with what I ended up with. :D

Code: Select all

enum { FALSE, TRUE };
enum { QUIT, THINK, GETCMD };

#ifdef _MSC_VER
typedef signed   __int32 s32;
typedef unsigned __int64 u64;
#else
#define s32 int
#define u64 long long
#endif 

#define BIT64&#40;x&#41;     (&#40;u64&#41;1 << &#40;x&#41;)
#define SQUARE64&#40;x&#41;  ((((&#40;x&#41; / 10&#41; - 2&#41; << 3&#41; + (&#40;x&#41; % 10 - 1&#41;)
#define SQUARE120&#40;x&#41; ((((&#40;x&#41; >> 3&#41; + 2&#41; * 10&#41; + ((&#40;x&#41; & 7&#41; + 1&#41;)

void Think&#40;void&#41;;
void Initialize&#40;void&#41;;
void GetCmd&#40;void&#41;;
s32 main&#40;void&#41;;

u64 dirKn&#91;64&#93;;
u64 dirKi&#91;64&#93;;

u64 dir7p&#91;64&#93;;
u64 dir9p&#91;64&#93;;
u64 dir7m&#91;64&#93;;
u64 dir9m&#91;64&#93;;
u64 dir1p&#91;64&#93;;
u64 dir8p&#91;64&#93;;
u64 dir1m&#91;64&#93;;
u64 dir8m&#91;64&#93;;

s32 mode = GETCMD;

u64 *const dirPtr&#91;8&#93; = &#123; dir7p, dir9p, dir7m, dir9m, dir1p, dir8p, dir1m, dir8m &#125;;

void Think&#40;) &#123;

&#125;

void GetCmd&#40;) &#123;
  mode = QUIT;
&#125;

void Initialize&#40;) &#123;
  s32 i, fs120, ts120, fs64, ts64;
  s32 dir&#91;8&#93; = &#123; 9, 11, -9, -11, 1, 10, -1, -10 &#125;;
  s32 din&#91;8&#93; = &#123; 8, 12, 19, 21, -8, -12, -19, -21 &#125;;
  s32 isOnBoard&#91;120&#93; = &#123;
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0 &#125;;

  for &#40;fs64 = 63; fs64 >= 0; fs64--) &#123;
    fs120 = SQUARE120&#40;fs64&#41;;
    dirKi&#91;fs64&#93; = 0;
    dirKn&#91;fs64&#93; = 0;
    for &#40;i = 0; i < 8; i++) &#123;
      ts120 = fs120 + din&#91;i&#93;;
      if &#40;isOnBoard&#91;ts120&#93;) &#123;
        dirKn&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
      &#125;
      dirPtr&#91;i&#93;&#91;fs64&#93; = 0;
      ts120 = fs120 + dir&#91;i&#93;;
      if &#40;isOnBoard&#91;ts120&#93;)  &#123;
        ts64 = SQUARE64&#40;ts120&#41;;
        dirKi&#91;fs64&#93; |= BIT64&#40;ts64&#41;;
        while&#40;1&#41; &#123;
          dirPtr&#91;i&#93;&#91;fs64&#93; |= BIT64&#40;ts64&#41;;
          ts120 += dir&#91;i&#93;;
          if (!isOnBoard&#91;ts120&#93;) break;
          ts64 = SQUARE64&#40;ts120&#41;;
        &#125; 
      &#125;
    &#125;
  &#125;
&#125;

s32 main&#40;)
&#123;
  Initialize&#40;);

  while &#40;mode&#41; &#123;
    if &#40;mode == THINK&#41; Think&#40;);
    GetCmd&#40;);
  &#125;
  return 0;
&#125;
Regards,
Mike

Michael Sherwin
Posts: 2799
Joined: Fri May 26, 2006 1:00 am
Location: OH, USA

Re: I'm not very happy with the do {} while() statement in C

Post by Michael Sherwin » Tue Feb 20, 2018 6:14 am

bob wrote: This thread is an example of poor programming that leads to convoluted code due to pedantic reasons such as objecting to the use of a goto (break) which makes the code much easier to read than the ugly do/white syntax proposed...
It might be ugly (to some) and smelly (to Lucas) and even look convoluted (to you) but in assembly it would be quite natural to jump over the code that one would not want to execute unnecessarily in order to continue. Or to delay looping back until after a necessary step is performed.

And this is not convoluted but just unfamiliar.

do {
something
} while (condition) first {
something
}

And then you do not need an ugly kludge of while(1) and an if statement to break out of it.

Which I also said that I was fine with. My suggestion was tongue in cheek as I did not expect someone to go right out and add it to their C/C++ implementation so I could continue writing my program, :lol:. I was just looking for a better solution and I got one. Thanks to the people that actually tried to help rather than just condemn.
Regards,
Mike

Michael Sherwin
Posts: 2799
Joined: Fri May 26, 2006 1:00 am
Location: OH, USA

Re: I'm not very happy with the do {} while() statement in C

Post by Michael Sherwin » Tue Feb 20, 2018 6:33 am

Okay Bob, Assuming that you still do not like the final draft of my initializing function then the appropriate saying would be, "put up or shut up". So how would you write it? And if you write it and I like it I will use it and give you full credit in the readme file. I'm the student and you are the professor/teacher, so teach. :D
Regards,
Mike

bob
Posts: 20346
Joined: Mon Feb 27, 2006 6:30 pm
Location: Birmingham, AL

Re: I'm not very happy with the do {} while() statement in C

Post by bob » Tue Feb 20, 2018 10:23 pm

Michael Sherwin wrote:Okay Bob, Assuming that you still do not like the final draft of my initializing function then the appropriate saying would be, "put up or shut up". So how would you write it? And if you write it and I like it I will use it and give you full credit in the readme file. I'm the student and you are the professor/teacher, so teach. :D
I would (and do) use EXACTLY the suggestion that was already given.

An if test with a break to exit the loop. Rather than using for (;;) { } however, I tend to use while (1) {} instead. Same thing. Grep for "while (1)" in Crafty source, you will find several examples. There are older examples where I have used do { } while (1); which is the same thing, using a break inside to exit. At some point, I decided that I liked the while (1) syntax better since it expresses the intent at the top of the loop, you don't have to search for the bottom to see if it is finite or not.

To me, this is about nothing more than readability. I think the while(1) and break is about as easy to interpret as it can be, when reading the source code. I just noticed that at some point I switched to while (FOREVER) which appears in many more places...

Just converted the few odd cases to all use while (FOREVER). Don't know how I missed those over the years...

Sven
Posts: 3578
Joined: Thu May 15, 2008 7:57 pm
Location: Berlin, Germany

Re: I'm not very happy with the do {} while() statement in C

Post by Sven » Tue Feb 20, 2018 10:37 pm

Michael Sherwin wrote:This is what I ended up with. I hope that I do not hurt Jumbo's feelings because I did not build up the final macros using simple macros but I did use the modulo operator as it has a counterpart in the other macro's '&'. Also there was a peanut shell with a bad peanut in it, :(. The modified do loop reintroduced the problem of double execution of SQUARE64(ts120) before entering and on entering the do loop. If that is the case then a while loop should just be used. That is unless the compiler is smart enough to jump over the second conversion in the do loop because SQUARE64(ts120) was called with the same parameter. But, I do not know if that is the case. :( But all in all I am happy with what I ended up with. :D
No feelings were hurt ;-) Double execution of SQUARE64(ts120) with identical parameter value would fall under the typical common subexpression optimization of even the most ancient compilers. In the given case, though, the parameter ts120 is modified within the loop (but not between its use before entering and on entering the loop). So I think compilers probably create code that almost corresponds to the "while (1) ... if (...) break" form - but why should we really care about that?

So the code snippet

Code: Select all

ts64 = SQUARE64&#40;ts120&#41;;
dirKi&#91;fs64&#93; |= BIT64&#40;ts64&#41;;
while&#40;1&#41; &#123;
  dirPtr&#91;i&#93;&#91;fs64&#93; |= BIT64&#40;ts64&#41;;
  ts120 += dir&#91;i&#93;;
  if (!isOnBoard&#91;ts120&#93;) break;
  ts64 = SQUARE64&#40;ts120&#41;;
&#125;
is semantically identical to

Code: Select all

dirKi&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
while&#40;1&#41; &#123;
  dirPtr&#91;i&#93;&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
  ts120 += dir&#91;i&#93;;
  if (!isOnBoard&#91;ts120&#93;) break;
&#125;
after realizing that ts64 is only a temporary variable. And that version can also be written with do-while, making the whole sequence really trivial and obvious. That's exactly what you want: register the current bit exactly once for the king, and register it once per step for a sliding piece until the incremented target square is off the board. Using a temporary ts64 variable complicates everything in my opinion since the conversion macro that is necessary to update ts64 requires ts120 to be a valid square, a condition that is also the loop condition.

Code: Select all

dirKi&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
do &#123;
  dirPtr&#91;i&#93;&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
  ts120 += dir&#91;i&#93;;
&#125; while &#40;isOnBoard&#91;ts120&#93;);
I would be surprised if anyone would prove this to be slower than the "while ... break" version.

AlvaroBegue
Posts: 884
Joined: Tue Mar 09, 2010 2:46 pm
Location: New York

Re: I'm not very happy with the do {} while() statement in C

Post by AlvaroBegue » Tue Feb 20, 2018 11:45 pm

Michael Sherwin wrote:[...]

And then you do not need an ugly kludge of while(1) and an if statement to break out of it.
while(1) with a break in the middle is an extremely flexible construct. When I was in college I took part in ACM programming competitions, and we used that construct all the time, in contexts like this one:

Code: Select all

while &#40;1&#41; &#123;
  bool success = read_some_input&#40;);
  if (!success&#41;
    break;
  process_input&#40;);
&#125;
I don't consider this a kludge at all.

Michael Sherwin
Posts: 2799
Joined: Fri May 26, 2006 1:00 am
Location: OH, USA

Re: I'm not very happy with the do {} while() statement in C

Post by Michael Sherwin » Tue Feb 20, 2018 11:52 pm

bob wrote:
Michael Sherwin wrote:Okay Bob, Assuming that you still do not like the final draft of my initializing function then the appropriate saying would be, "put up or shut up". So how would you write it? And if you write it and I like it I will use it and give you full credit in the readme file. I'm the student and you are the professor/teacher, so teach. :D
I would (and do) use EXACTLY the suggestion that was already given.

An if test with a break to exit the loop. Rather than using for (;;) { } however, I tend to use while (1) {} instead. Same thing. Grep for "while (1)" in Crafty source, you will find several examples. There are older examples where I have used do { } while (1); which is the same thing, using a break inside to exit. At some point, I decided that I liked the while (1) syntax better since it expresses the intent at the top of the loop, you don't have to search for the bottom to see if it is finite or not.

To me, this is about nothing more than readability. I think the while(1) and break is about as easy to interpret as it can be, when reading the source code. I just noticed that at some point I switched to while (FOREVER) which appears in many more places...

Just converted the few odd cases to all use while (FOREVER). Don't know how I missed those over the years...
Thanks Bob, I appreciate the explanation!
Regards,
Mike

Michael Sherwin
Posts: 2799
Joined: Fri May 26, 2006 1:00 am
Location: OH, USA

Re: I'm not very happy with the do {} while() statement in C

Post by Michael Sherwin » Tue Feb 20, 2018 11:59 pm

Sven wrote:
Michael Sherwin wrote:This is what I ended up with. I hope that I do not hurt Jumbo's feelings because I did not build up the final macros using simple macros but I did use the modulo operator as it has a counterpart in the other macro's '&'. Also there was a peanut shell with a bad peanut in it, :(. The modified do loop reintroduced the problem of double execution of SQUARE64(ts120) before entering and on entering the do loop. If that is the case then a while loop should just be used. That is unless the compiler is smart enough to jump over the second conversion in the do loop because SQUARE64(ts120) was called with the same parameter. But, I do not know if that is the case. :( But all in all I am happy with what I ended up with. :D
No feelings were hurt ;-) Double execution of SQUARE64(ts120) with identical parameter value would fall under the typical common subexpression optimization of even the most ancient compilers. In the given case, though, the parameter ts120 is modified within the loop (but not between its use before entering and on entering the loop). So I think compilers probably create code that almost corresponds to the "while (1) ... if (...) break" form - but why should we really care about that?

So the code snippet

Code: Select all

ts64 = SQUARE64&#40;ts120&#41;;
dirKi&#91;fs64&#93; |= BIT64&#40;ts64&#41;;
while&#40;1&#41; &#123;
  dirPtr&#91;i&#93;&#91;fs64&#93; |= BIT64&#40;ts64&#41;;
  ts120 += dir&#91;i&#93;;
  if (!isOnBoard&#91;ts120&#93;) break;
  ts64 = SQUARE64&#40;ts120&#41;;
&#125;
is semantically identical to

Code: Select all

dirKi&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
while&#40;1&#41; &#123;
  dirPtr&#91;i&#93;&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
  ts120 += dir&#91;i&#93;;
  if (!isOnBoard&#91;ts120&#93;) break;
&#125;
after realizing that ts64 is only a temporary variable. And that version can also be written with do-while, making the whole sequence really trivial and obvious. That's exactly what you want: register the current bit exactly once for the king, and register it once per step for a sliding piece until the incremented target square is off the board. Using a temporary ts64 variable complicates everything in my opinion since the conversion macro that is necessary to update ts64 requires ts120 to be a valid square, a condition that is also the loop condition.

Code: Select all

dirKi&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
do &#123;
  dirPtr&#91;i&#93;&#91;fs64&#93; |= BIT64&#40;SQUARE64&#40;ts120&#41;);
  ts120 += dir&#91;i&#93;;
&#125; while &#40;isOnBoard&#91;ts120&#93;);
I would be surprised if anyone would prove this to be slower than the "while ... break" version.
Really cool Sven! 8-) So basically write code that looks good and hope the compiler is smart enough because it should be and probably is. If only we could have that much faith in our politicians. I'll be switching to the Jumbo way! Thanks :D
Regards,
Mike

Michael Sherwin
Posts: 2799
Joined: Fri May 26, 2006 1:00 am
Location: OH, USA

Re: I'm not very happy with the do {} while() statement in C

Post by Michael Sherwin » Wed Feb 21, 2018 12:15 am

AlvaroBegue wrote:
Michael Sherwin wrote:[...]

And then you do not need an ugly kludge of while(1) and an if statement to break out of it.
while(1) with a break in the middle is an extremely flexible construct. When I was in college I took part in ACM programming competitions, and we used that construct all the time, in contexts like this one:

Code: Select all

while &#40;1&#41; &#123;
  bool success = read_some_input&#40;);
  if (!success&#41;
    break;
  process_input&#40;);
&#125;
I don't consider this a kludge at all.
I understand your point of view. I read about the development of C and B decades ago. I believe at some point the case statement was added and at some point break and continue were added to solve improper use of goto tendencies. But that was so long ago I'm not sure about that.

While(1) etc is a learned, taught and commonly accepted practice to solve a problem. By definition it is a kludge. Although a good one that is easy to read and understand.

However, you have provided a better word for my suggestion.

Code: Select all

do &#123;
  bool success = read_some_input&#40;);
&#125; while&#40;success&#41; process &#123;
  process_input&#40;);
&#125;
:D
Regards,
Mike

Post Reply