• Advertisement
Sign in to follow this  

Any point to this type of code?

This topic is 3292 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ok, this is one type of coding I've seen, and I have NO idea why you'd do this.
AddVector( Vector *a, Vector *b, Vector *c )
{
const float a_x = a->x;
const float a_y = a->y;
const float a_z = a->z;
const float b_x = b->x;
const float b_y = b->y;
const float b_z = b->z;
const float c_x = a_x + b_x;
const float c_y = a_y + b_y;
const float c_z = a_z + b_z;
c->x = c_x;
c->y = c_y;
c->z = c_z;
}



Now, lets assume that the pointer notation can't be substituted for references or values. What is the point of making all those 'const float' declarations, that IMHO clutter up the code? Now, assume this type of coding takes place in a bigger function. Now you can't call AddVector(at,offset) because you only have at_x at_y at_z and offset_x offset_y offset_z. So you have to inline that function by hand(or construct new vectors just for the sake of that function call). So again, I wonder, what could possibly be the reason for doing this? and while I'm thinking about it.
#define foo() do {blah} while(0)


(lame... that was supposed to be a multi-line #define) What is the point of the "do/while"? isnt the {} enough to make the scoping work? [Edited by - KulSeran on January 14, 2009 3:33:15 AM]

Share this post


Link to post
Share on other sites
Advertisement
1) Yes, that code is very unessecary and doesn't really accomplish what it's supposed to do; make the code more readable.
And also, the parameters should probably be references instead of pointers (that is, if it's proper C++).

2) do/while is the older version of just while and therefore you don't see it alot these days.
Having just the {} would merely create a sub-routine, not a loop.

Share this post


Link to post
Share on other sites
Quote:
Original post by KulSeran
What is the point of the "do/while"? isnt the {} enough to make the scoping work?
The point is to require a semicolon after the macro.

#define foo(x) { \
multi; \
line; \
block; \
}

#define bar(x) do { \
multi; \
line; \
block; \
} while (0)
...

foo(42) // <-- no semicolon required here
bar(13) // <-- won't compile unless you add the semicolon

Share this post


Link to post
Share on other sites
yeah. whoops. typo on my part. shoulda been while(0) not while(1) and so thanks Oxyd. I never thought of that implication. Interesting trick, even if macros make the code hard to read/debug.

Share this post


Link to post
Share on other sites
If you use a modern compiler the while loop will be optimized away, so there is no point whatsoever in having it.

Share this post


Link to post
Share on other sites
Quote:
Original post by HomerSp
2) do/while is the older version of just while and therefore you don't see it alot these days.
Having just the {} would merely create a sub-routine, not a loop.
It's not older, it has different behaviour. And personally, I see it all the time...

Quote:
Original post by HomerSp
If you use a modern compiler the while loop will be optimized away, so there is no point whatsoever in having it.
Apart from requiring the semicolon at the end of the macro. Even using an ancient compiler, it'll still compile away.

Share this post


Link to post
Share on other sites
The do {} while(); defines when the loop condition modifiers are tested

running the block




do
{
//blah blah blah
}while(0); //tested after the first iteration is ran

/*will execute //blah blah blah */


while(0) // tested before the first iteration is ran
{
//blah
};




will skip over blah

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
It's not older, it has different behaviour. And personally, I see it all the time...

I suppose you're right, I never use it myself but I can see how it would behave differently (which could be useful) in a program.

So pardon the false explanation.


do {
execute
} while(0);




will be executed once

while

while(0)
{
execute
}




will be optimized away totally.

Edit: ...which I now see is what FritzMar just explained.

Share this post


Link to post
Share on other sites
The first thing you posted is an optimisation by someone who probably went overboard with const. accessing a->x through a pointer more than once may have generated worse code than accessing it through the pointer only once, and storing the result in a local variable.

The do .. while thing is to make this kind of code work:
if (expression)
foo();
else
bar();

if foo did not use the while(0) trick, and instead had multiple statements within { and } in it, then the semicolon after what looks like just a function call, would cause the else not to match the if.
I would be very surtprised if do .. while(0) is ever responsible for even a single additional assembly instruction, probably even in debug builds, on just about any compiler. A do..while is as simple as a single test and branch, and when the test is always false, then it's effectively a branch-never instruction, of which there is no such thing.

Share this post


Link to post
Share on other sites
Quote:
Original post by KulSeran
Ok, this is one type of coding I've seen, and I have NO idea why you'd do this.
*** Source Snippet Removed ***
Now, lets assume that the pointer notation can't be substituted for references or values.

This looks idiomatic approach when avoiding certain issues, such as aliasing. It may or may not be warranted in this case. See slides 38-40 (PPT).


Quote:
What is the point of the "do/while"? isnt the {} enough to make the scoping work?


It's a macro guard (PDF, may require download).

Share this post


Link to post
Share on other sites
Yeah. I still don't see how the const stuff helps. Maybe it is something to work around some compiler issue that causes it to spit out bad code, which I could understand considering the do{}while(0) on the compiler we use at work DOES in fact do a

topofLoop:
/...blah
li $a0, 0
bne $a0, topOfLoop

in *edit* the debug builds.


--edit: Reading the Ericson document. That COULD just be the answer I was looking for.
-- Yup. Probably just a prefetch trick to make the compiler do all the loads before calling any of the math functions.

[Edited by - KulSeran on January 14, 2009 1:37:00 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by kittycat768
I've always used while(1). Wouldn't while(0) immediately break? It sure does in my programs.


while(0) will break. do..while(0) will execute once, because do..while always executes at least once.

Share this post


Link to post
Share on other sites
Quote:
Original post by someboddy
Quote:
Original post by kittycat768
I've always used while(1). Wouldn't while(0) immediately break? It sure does in my programs.


while(0) will break. do..while(0) will execute once, because do..while always executes at least once.


Then why do people use do {} while(0)? I know that I've seen it a few times. My code executes only once all by itself w/o the help of do {} while(0).

Share this post


Link to post
Share on other sites
Quote:
Original post by kittycat768
Then why do people use do {} while(0)? I know that I've seen it a few times. My code executes only once all by itself w/o the help of do {} while(0).


Quote:
Original post by Oxyd
The point is to require a semicolon after the macro.

#define foo(x) { multi; line; block; }

#define bar(x) do { multi; line; block; } while (0)
...

foo(42) // <-- no semicolon required here
bar(13) // <-- won't compile unless you add the semicolon



Share this post


Link to post
Share on other sites
Quote:
Original post by binchawpz
Quote:
Original post by kittycat768
Then why do people use do {} while(0)? I know that I've seen it a few times. My code executes only once all by itself w/o the help of do {} while(0).


Quote:
Original post by Oxyd
The point is to require a semicolon after the macro.

#define foo(x) { multi; line; block; }

#define bar(x) do { multi; line; block; } while (0)
...

foo(42) // <-- no semicolon required here
bar(13) // <-- won't compile unless you add the semicolon


I guess that makes sense. I just leave out the last semi-colon when my I make my macros. I know exactly what you're talking about now. Thank you for answering my question :P

Share this post


Link to post
Share on other sites
Quote:
Original post by kittycat768
Then why do people use do {} while(0)? I know that I've seen it a few times. My code executes only once all by itself w/o the help of do {} while(0).


It's so you can go:


#define foo(x) do { blah(x); } while (0)

...

if (a)
foo(x);
else
something_else(x);



If you didn't do the do {...} while(0) trick, the if() wouldn't compile because you'd have an extra semi-colon.

Not that I recommend you write code like that. An inline function is a better idea in most cases.

Share this post


Link to post
Share on other sites
Quote:
Original post by Codeka
Quote:
Original post by kittycat768
Then why do people use do {} while(0)? I know that I've seen it a few times. My code executes only once all by itself w/o the help of do {} while(0).


It's so you can go:

*** Source Snippet Removed ***

If you didn't do the do {...} while(0) trick, the if() wouldn't compile because you'd have an extra semi-colon.
Thanks for re-posting that example. Using the [source][/source] tags rather than the [code][/code] tags makes it show up better huh!

I swear my posts must be invisible sometimes.

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
accessing a->x through a pointer more than once may have generated worse code than accessing it through the pointer only once, and storing the result in a local variable.


While this statement is true, I have no idea why you would need to access each member more than once.


AddVector( Vector *a, Vector *b, Vector *c )
{
c->x = a->x + b->x;
c->y = a->y + b->y;
c->z = a->z + b->z;
}



My opinion is that the original version of this function is just terribly written.

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
I swear my posts must be invisible sometimes.


Woah, sorry! That was weird I didn't see your example :p~

Share this post


Link to post
Share on other sites
The first piece of code might be a bit bloat, but maybe it has been tested to be faster on some machines. Optimisers are riddles sometimes.

But that style can also be used to split up bigger formulas, like
const float i = sqrtf (x*x + powf (y,z)) / 77.0f;

, then
const float square_x  = x * x;
const float pow_xz = powf (y,z);
const float inner_sum = square_x + pow_xz;
const float i = sqrtf (inner_sum) / 77.0f;

. Sometimes, formulas get really big (for a real life example, look at A Practical Analytical model for Daylight by Preetham, et al).

In the end, it doesn't really matter if you split up the computation or not, as compilers will generally transform your code into (or something similar to) static single assignment form (SSA).

About all the consts: This helps to get the program const-correct, which is said to make the program more verifiable, and decrease unintended side effects.




Sidenote: There's an optimisation left:

const float square_x  = x * x;
const float pow_xz = powf (y,z);
const float inner_sum = square_x + pow_xz;
const float inv77 = 1.0f / 77.0f;
const float i = sqrtf (inner_sum) * inv77;


Compilers won't do that optimisation for you (unless you beg for it, for example with gcc's -ffast-math and -freciprocal-math) as x/77.0 and x*(1.0/77.0) can yield different results in floating point arithmetic (and some applications are very sensitive to NaN's, inf's and co, i.e. they might rely on them; see What every computer scientist should know about floating point arithmetic). Also, for the same reason, optimisers won't reorder the operands (unless e.g. with gcc's -fassociative-math).

See also this for an overview on optimization flags and what compilers can do today.

Also, here is Agner Fog's optimisation manual.


edit:typo

Share this post


Link to post
Share on other sites
Quote:
Original post by scjohnno
Quote:
Original post by iMalc
accessing a->x through a pointer more than once may have generated worse code than accessing it through the pointer only once, and storing the result in a local variable.


While this statement is true, I have no idea why you would need to access each member more than once.

AddVector( Vector *a, Vector *b, Vector *c )
{
c->x = a->x + b->x;
c->y = a->y + b->y;
c->z = a->z + b->z;
}

My opinion is that the original version of this function is just terribly written.


Aliasing is the problem. Compiler (for general case) cannot guarantee that the following won't happen:
Vector v;

AddVector(&v, &v, &v);


Or even something more atrocious:

Vector * w = &v;
w = (Vector*)(((char *)w)+sizeof(w.x));
AddVector(&v, &v, &w);
which might be the case when doing some form of swizzling or similar on an array or something.

Original code should produce expected results even in case of aliasing, although it can still corrupt inputs.

The const modifier might be redundant as far as compiler is concerned, but is valid idiom. It gives programmer clear notice that values are assigned only once.
The set of c_ variables might also be redundant, but they might exist to improve separation between reads and writes to improve scheduling or pipelining.

[Edited by - Antheus on January 15, 2009 5:33:46 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
The const modifier might be redundant as far as compiler is concerned


For the compiler it might not make a difference (like with begging for inline), but sometimes const qualifiers save the day of a programmer, who is subject to mistyping and misunderstanding, and they could help to prove code-correctness (that includes pieces of code). Personally, I am a fan of single assignment and related idioms/techniques.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement