OGRE coding and style guidelines

Discussion area about developing or extending OGRE, adding plugins for it or building applications on it. No newbie questions please, use the Help forum for that.
genva
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 1603
Joined: Wed Oct 20, 2004 7:54 am
Location: Beijing, China
x 1

Post by genva »

Sometimes I'd like write test code like this:

Code: Select all

//*/
    Code block 1
/*/
    Code block 2
//*/
and

Code: Select all

/*/
    Code block 1
/*/
    Code block 2
//*/
You can see the magic in a C++ syntax highlighting editor :).
OvermindDL1
Gnome
Posts: 333
Joined: Sun Sep 25, 2005 7:55 pm

Post by OvermindDL1 »

Heh, cute. :)
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

Wow, genva!
Thanks for the tip, it really works like magic! :D
User avatar
joshcryer
Gnome
Posts: 351
Joined: Wed Oct 13, 2004 8:22 am

Post by joshcryer »

Hehe! That is cute. :)

Cool tip.
User avatar
felix
Gnoblar
Posts: 23
Joined: Fri Oct 14, 2005 10:57 am
Location: Stockholm, Sweden

Post by felix »

Excellent coding guidelines!

One minor thing caught my eye, am I the only one who prefer the inequality operator for loop exit conditions? E.g. "for (int i = 0; i != n; ++i)"
Faster and more precise post-condition and also consistent with how you use iterator constructs.
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7

Post by haffax »

Guess < is more flexible than !=, sometimes one does a ++i in the loop too (there are valid reasons for this) And i != n can be jumped over by this.
Using for on iterators is an idiom in itself imho. I never thought about != being faster than <, is this the general case, really?
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Post by Kojack »

Using != instead of < will have no performance difference for the int based example given.
!= would in most cases fail for a float based for loop, and could fail for even int loops if the increment value is not 1.
User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19269
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
x 66

Post by sinbad »

!= is not faster than <. != can be generated into a 'jne' assembler opcode, < can be converted into a 'jb'. They're the same number of cycles and therefore completely identical.

That is assuming the compiler isn't smart enough to put the original count in a register, decrementing and using jnz for both (which is likely). Check your facts before claiming things like this. :)
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Post by Kojack »

The following 2 loops:

Code: Select all

for(int i=0;i<100;i++)
{
   x++;
}



for(int i=0;i!=100;i++)
{
   x++;
}
generate the following 2 blocks of assembly (using visual studio 2005 debug mode):

Code: Select all

     mov   DWORD PTR _i$23092[ebp], 0
     jmp   SHORT $LN6@main
$LN5@main:
     mov   eax, DWORD PTR _i$23092[ebp]
     add   eax, 1
     mov   DWORD PTR _i$23092[ebp], eax
$LN6@main:
     cmp   DWORD PTR _i$23092[ebp], 100		; 00000064H
     jge   SHORT $LN4@main

     mov   eax, DWORD PTR _x$[ebp]
     add   eax, 1
     mov   DWORD PTR _x$[ebp], eax
     jmp   SHORT $LN5@main
$LN4@main:



     mov   DWORD PTR _i$23096[ebp], 0
     jmp   SHORT $LN3@main
$LN2@main:
     mov   eax, DWORD PTR _i$23096[ebp]
     add   eax, 1
     mov   DWORD PTR _i$23096[ebp], eax
$LN3@main:
     cmp   DWORD PTR _i$23096[ebp], 100		; 00000064H
     je	SHORT $LN1@main
     mov   eax, DWORD PTR _x$[ebp]
     add   eax, 1
     mov   DWORD PTR _x$[ebp], eax
     jmp   SHORT $LN2@main
$LN1@main:
Both blocks are identical except for the jge and je instructions, which should (as far as I know, intel have become very good at hiding timing reference docs, spent a while yesterday trying to fine some) have identical performance.
Bloodypriest
Goblin
Posts: 223
Joined: Thu Aug 18, 2005 2:54 pm

Post by Bloodypriest »

Both blocks are identical except for the jge and je instructions, which should (as far as I know, intel have become very good at hiding timing reference docs, spent a while yesterday trying to fine some) have identical performance.
Didn't the Ralf Brown's interrupt list had a doc that documented x86 opcodes and their cycle count. It may be old and targeted at 486 but pentium 4 are still x86s.

I wish I could pick up assembly again. So many new instruction sets to learn... Maybe I'll have the time... one day...
User avatar
felix
Gnoblar
Posts: 23
Joined: Fri Oct 14, 2005 10:57 am
Location: Stockholm, Sweden

Post by felix »

Ok "faster" was maybe not the right word. Only a slight complexity difference at the gate level in the ALU maybe. ;)

The two ideoms would be:
A) for (<while loop criteria>)
and
B) for (<until exit criteria>)

Iterator loops usually fall into the second category , and float/double usually in the first one. My point is, int loops could be one or the other, depending on the situation. My personal rule of thumn is: choose the simpler one, and that is in most cases the exit criteria.
User avatar
DWORD
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 1365
Joined: Tue Sep 07, 2004 12:43 pm
Location: Aalborg, Denmark

Post by DWORD »

Two benefits of using < rather than != : Half number of key strokes, and should you later decide to do an i++ inside the loop it will still work as expected. :twisted:
klauss
Hobgoblin
Posts: 559
Joined: Wed Oct 19, 2005 4:57 pm
Location: LS87, Buenos Aires, República Argentina.

Post by klauss »

Kojack wrote:Both blocks are identical except for the jge and je instructions, which should (as far as I know, intel have become very good at hiding timing reference docs, spent a while yesterday trying to fine some) have identical performance.
The instruction reference has an appendix with latencies and thoughput, which is as close as you can get to cycle counts. Since modern processors are so dynamic, instructions don't take a fixed amount of cycles to execute: memory addressing, cache misses, even uOp starvation can kill your numbers.

On jumps, the primary concern is misprediction, which leads to a flush of the entire pipeline (pipeline stall), prefetch cache flush, and thus heavy uOp starvation at the start of the next cycle (besides the serious delay, which IIRC can range from 7 to 12 cycles). Since all jxx instructions share this problem, and since all of them also share a dependenci on the eflags register, all are effectively equivalent.

In any case, debug assembly output should not be used since it does not expose the true situation: optimization is exactly what is being measured, so one must have compiler optimization enabled or the measure is meaningless.


Now... about < rather than != ... just try to prove your cycle ends with != - you'll have to add cycle preconditions. With <, fewer preconditions are necessary. That's a hint: < is safer than != (less oportunities for making mistakes). It is also a hint that both loops are not functionally equivalent, and I really doubt the compiler will be able to tell when the extra preconditions are granted (so that one may be converted to the other) most of the time.
Oíd mortales, el grito sagrado...
Hey! What is it with that that?
Wing Commander Universe
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Post by Kojack »

Yes, debug isn't going to show optimised code. But in optimised code the behavior will be affected by other surrounding code, and what you do with the results of the loops, so it's impossible to say which is faster in a generic sense.

Anyway, here's the release mode version. I don't want to polute the thread with asm, but the result is interesting...

First, just a release build: That's both loops together. As expected, VC saw that the loops had no side effects, so it just threw them away completely. :)

To avoid that, make x a volatile (x is therefore not optimised, and the loops must be run):

Code: Select all

	mov	eax, 100				; 00000064H
$LL6@main:
	add	DWORD PTR _x$[esp+1564], 1
	sub	eax, 1
	jne	SHORT $LL6@main



	mov	eax, 100				; 00000064H
$LL3@main:
	add	DWORD PTR _x$[esp+1564], 1
	sub	eax, 1
	jne	SHORT $LL3@main
Visual C++ realised that both loops were effectively doing the same thing, and that the loop index had no direct effect on the value of x, so it made both be identical loops which count backwards from 100.
klauss
Hobgoblin
Posts: 559
Joined: Wed Oct 19, 2005 4:57 pm
Location: LS87, Buenos Aires, República Argentina.

Post by klauss »

Smart guy... I don't think VC6 is that smart.
Oíd mortales, el grito sagrado...
Hey! What is it with that that?
Wing Commander Universe
Vrej
Gnoblar
Posts: 10
Joined: Wed Sep 27, 2006 2:38 pm
Location: Canada

Post by Vrej »

There is a mix of spaces for indentation and tabs in the source and examples.

One thing I hate is that some people have set their editor to preserve tabs and others convert tabs to spaces. Even if tabs are converted to spaces, you can change the number of spaces.

Personally, I prefer tabs so I'll keep tabs in my own stuff.
User avatar
Frenetic
Bugbear
Posts: 806
Joined: Fri Feb 03, 2006 7:08 am

Post by Frenetic »

Question! Why is

Object* example = 0;

preferred over NULL? I always use NULL when assigning to pointers because pointers should not be treated like integers. NULL looks special, 0 makes the variable look like an integer.

I'm not saying my way is better, just wondering what the reason is for doing it the other way.
User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19269
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
x 66

Post by sinbad »

NULL is typically the old-style C way, and C++'s strong typing potentially has problems with some compilers definition of NULL as (void*)0 - something that C would ignore but C++ does not if you assign it to something not void*. I'm not sure this is actually an issue any more, but using 0 over NULL is still generally considered better C++ style.
User avatar
Frenetic
Bugbear
Posts: 806
Joined: Fri Feb 03, 2006 7:08 am

Post by Frenetic »

I see. I doubt its really an issue, because if such a bug pops up, you can just #undef and change NULL to 0.

Also, IIRC there is a proposed nullptr keyword in the works for C++0x. So apparently I'm not the only one who thinks using "0" stresses the semantics a little. ;)
User avatar
Invader Zim
Halfling
Posts: 55
Joined: Fri Mar 25, 2005 11:42 am
Location: Oslo, Norway

Post by Invader Zim »

I know it's thread necromancy, but I'd just like to add one problem related to NULL that we've had: When you're passing variable arguments to a function, make sure to use NULL, not 0, for pointers. Otherwise, on a 64 bit platform, the caller will put a 32-bit 0 on the stack, but the callee, expecting a pointer, pulls out 64 bits, and you get a pointer like 0xcccccccc00000000 or some other random first 4 bytes.

Not only are varargs evil. On 64 bit platforms, they became a lot more so.
Kerion
Goblin
Posts: 235
Joined: Wed Feb 05, 2003 5:49 am

Post by Kerion »

I think the 64-bit pointer issue is exactly why there is a keyword being created for this in C++ 0x. They want to move completely away from #define's when possible, and NULL is a define, so 'nullptr' will be used. Why nullptr? I have no idea, just use the 'null' keyword from C#/Java, but oh well.
User avatar
FrameFever
Platinum Sponsor
Platinum Sponsor
Posts: 414
Joined: Fri Apr 27, 2007 10:05 am

Post by FrameFever »

sinbad wrote:NULL is typically the old-style C way, and C++'s strong typing potentially has problems with some compilers definition of NULL as (void*)0 - something that C would ignore but C++ does not if you assign it to something not void*. I'm not sure this is actually an issue any more, but using 0 over NULL is still generally considered better C++ style.
yes it is, but you use NULL in OGRE, so what should we think now?

Code: Select all

Node* Node::removeChild(Node* child)
    {
        if (child)
        {
            ChildNodeMap::iterator i = mChildren.find(child->getName());
            // ensure it's our child
            if (i != mChildren.end() && i->second == child)
            {
                // cancel any pending update
                cancelUpdate(child);

                mChildren.erase(i);
                child->setParent(NULL);
            }
        }
        return child;
    }
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

You shouldn't think, you should patch. :wink:
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
FrameFever
Platinum Sponsor
Platinum Sponsor
Posts: 414
Joined: Fri Apr 27, 2007 10:05 am

Post by FrameFever »

i searched the forum, but if found no thread like:"how can I submit a patch?"
User avatar
FrameFever
Platinum Sponsor
Platinum Sponsor
Posts: 414
Joined: Fri Apr 27, 2007 10:05 am

Post by FrameFever »

other Question is it common style do use void in a function list, where is no parameter?

Code: Select all

foo(void);
should I use that, or not, does it make any difference in compiling?
maybe faster? :D