Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411804 Posts in 69416 Topics- by 58462 Members - Latest Member: Moko1910

May 28, 2024, 11:45:24 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Coding styles
Pages: 1 ... 4 5 [6] 7
Print
Author Topic: Coding styles  (Read 18907 times)
Sos
Level 8
***


I make bad games


View Profile WWW
« Reply #100 on: March 05, 2010, 05:25:38 AM »

I actually worked with several thousands lines of code for several games where they maintained game states with floating point numbers.
Code:
if(state == 1.1) {
   // hundreds of lines of code
} else if(state = 1.15) {
   // tons of code
    if(substate == 0.5) {
        // just to make things more interestings
    } else if(substate == 0.6) {
    }
} else {
}
This MIGHT work (but might just not on other machine), but this-> "if(state = 1.15)" definately wont. You shouldn't compare floats like that, even 1.0==1.0 might fail (as the reference manual says)
Logged

Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #101 on: March 05, 2010, 06:01:28 AM »

Code:
typedef enum _EBlendMode
{
   BM_MIN = -1,
   BM_ADD,
   BM_SUBTRACT,
   BM_MAX
} EBlendMode;

That typedef enum construct is a C thing, and is totally unnecessary in C++.  It's even a little dangerous, since you create two global names for the enum _EBlendMode (leading underscore followed by capital is also reserved for the compiler, watch this one), and EBlendMode.  This doesn't happen in C, since _EBlendMode goes into something called the "tag namespace" and needs to preceded by enum in order to access it.  Unless you need to include this header in a C program, this is actually a harmful construct in C++.

Quote
I typedefine f32 as float, s32 as signed int, etc. It's useful for remembering how big everything is and it's useful in calculations.

float isn't necessarily 32 bits, nor is signed int.  This is a very misleading convention.
 
Quote
I always use ++i in loops as it is slightly faster.

It's only faster if i is of some type that defines both operator ++s and the postfix ++ has to do more work than the prefix one. Many iterator types fit this description, but not all.  For the built-in types, it isn't any faster.  I'm not saything there's anything wrong with doing that, but your argument is bogus.
Logged



What would John Carmack do?
increpare
Guest
« Reply #102 on: March 05, 2010, 12:15:09 PM »

You shouldn't compare floats like that, even 1.0==1.0 might fail (as the reference manual says)
You have a citation for that?
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #103 on: March 05, 2010, 01:59:57 PM »

You shouldn't compare floats like that, even 1.0==1.0 might fail (as the reference manual says)
{{fact|date=March 2010}}
There, fixed that for you.
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
John Nesky
Level 10
*****


aka shaktool


View Profile WWW
« Reply #104 on: March 05, 2010, 02:06:38 PM »

I think it's technically OK to use floats like this as long as you only use the assignment and comparison operators. Never use math on floats before testing for equivalence.

See:
http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.18
Logged
Glaiel-Gamer
Guest
« Reply #105 on: March 05, 2010, 02:09:40 PM »

You shouldn't compare floats like that, even 1.0==1.0 might fail (as the reference manual says)
You have a citation for that?

if you did 1.0 == 1.0 the compiler would probably optimize that out.

if you did float f = 1.0, then tested for equality with 1.0, that could fail because 1.0 is a double and f is a float, so something has to be converted for there to be a comparison (if it converts 1.0 to a float, you are PROBABLY good [it's the same conversion used to store 1.0 in the float in the first place], if it converts f to a double, you are probably not good).

IMO, == should have just been disabled for floats.
(if you really want to compare 2 floats if == is disabled, there's always *((int*)(&f1)) == *((int*)(&f2))
« Last Edit: March 05, 2010, 02:13:17 PM by Glaiel-Gamer » Logged
Glaiel-Gamer
Guest
« Reply #106 on: March 05, 2010, 02:15:12 PM »

float isn't necessarily 32 bits, nor is signed int.  This is a very misleading convention.

I disagree here. This means if int is default 64, all you need to do is change the typedef to
typedef  __int32 s32

rather than locate every time you use int and change it

(I do then when dealing with file loading code. In game it usually doesn't matter the exact size of the int if it's sufficiently large)
Logged
st33d
Guest
« Reply #107 on: March 05, 2010, 02:18:08 PM »

What Every Computer Scientist Should Know About Floating Point Numbers

Personally, it's not okay to run a state machine in such a fashion.

It's fucking stupid. A sub-state should be a different state variable. Using floating point numbers for states betrays a misunderstanding of how floating point variables are used in a computer and educates every programmer who maintains that code to be equally stupid.
Logged
st33d
Guest
« Reply #108 on: March 05, 2010, 02:22:41 PM »

On another note, my dad once told me that he used to work for a bank. His task was to deal with how the interest on your money was calculated despite the fact that you obviously couldn't store the value of money as a standard floating point number. He pretty much had to engineer his own version of a floating point datatype to manage the situation.

I'll have to ask him about it again one of these days, it was a lecture I received before I was a career programmer, so most of it went over my head. It involved a lot of talk about how the mantissa and the exponent actually worked.
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #109 on: March 05, 2010, 02:46:39 PM »

float isn't necessarily 32 bits, nor is signed int.  This is a very misleading convention.

I disagree here. This means if int is default 64, all you need to do is change the typedef to
typedef  __int32 s32

rather than locate every time you use int and change it

(I do then when dealing with file loading code. In game it usually doesn't matter the exact size of the int if it's sufficiently large)

I think you actually agree with me.  I'm saying that if you want to claim a type is 32 bits, use a type that guaranteed to be 32 bits.  If I were working with a type called s32, I would expect it to be 32 bits, and I shouldn't have to go fix the typedef to make sure it is.

Note however, that __int32 is a non-portable compiler extension.  C99 defines a standard 32 bit integer type called int32_t in <stdint.h>.  This header will be adopted into C++1x (I'm pretty sure) which means you won't need the typedef at all anyway.
Logged



What would John Carmack do?
Radnom
Level 8
***


BANNED


View Profile
« Reply #110 on: March 05, 2010, 03:55:27 PM »

Code:
typedef enum _EBlendMode
{
   BM_MIN = -1,
   BM_ADD,
   BM_SUBTRACT,
   BM_MAX
} EBlendMode;

That typedef enum construct is a C thing, and is totally unnecessary in C++.  It's even a little dangerous, since you create two global names for the enum _EBlendMode (leading underscore followed by capital is also reserved for the compiler, watch this one), and EBlendMode.  This doesn't happen in C, since _EBlendMode goes into something called the "tag namespace" and needs to preceded by enum in order to access it.  Unless you need to include this header in a C program, this is actually a harmful construct in C++.
Ah! Habit I picked up from my school's coding standards. I missed where we were told it was a good idea so I can't remember what the tutor's reasoning was. I'll stop doing this.

Quote
I typedefine f32 as float, s32 as signed int, etc. It's useful for remembering how big everything is and it's useful in calculations.

float isn't necessarily 32 bits, nor is signed int.  This is a very misleading convention.

Which is exactly why I do this! if I'm using a system where that's not the case, I can change the types.h typedefs and all my code still (presumably) works.

I don't know of any types that are guaranteed to be a specific size so this method works for me.

Quote
I always use ++i in loops as it is slightly faster.

It's only faster if i is of some type that defines both operator ++s and the postfix ++ has to do more work than the prefix one. Many iterator types fit this description, but not all.  For the built-in types, it isn't any faster.  I'm not saything there's anything wrong with doing that, but your argument is bogus.
Really? Dang, everything I've been taught is a lie!  Tongue I was still curious about this topic so I've searched a bunch of other topics covering it a bit more thoroughly (but gave up as my internet is currently going dial-up speed and I'm impatient).

I'm still going to stick with the prefix ++ as it is tidier code, in my opinion. The postfix ++ should be used in very specific circumstances due to its nature.

It's always interesting reading your code related posts, Average, where did you get your knowledge pool from?
Logged

Glaiel-Gamer
Guest
« Reply #111 on: March 05, 2010, 04:11:22 PM »

Note however, that __int32 is a non-portable compiler extension.  C99 defines a standard 32 bit integer type called int32_t in <stdint.h>.  This header will be adopted into C++1x (I'm pretty sure) which means you won't need the typedef at all anyway.

Ya I knew that, I just forgot the form. I compile on GCC and MSVC so I make sure everything I write is MOSTLY portable.
Logged
Glaiel-Gamer
Guest
« Reply #112 on: March 05, 2010, 04:33:58 PM »

oh ya another rule I have: if the compiler can optimize it, don't worry about it

++i, i++ at the end of a for loop, the compiler WILL optimize (if i is an iterator, maybe, but i'm less sure so I do ++it to be safe)

i usually do i++ cause it reads more naturally after doing that for so many years


same for stuff like

a += b;
a *= c;
a %= n;

rather than a = (a+b)*c%n
while the second is "faster", it's less readable (not necessarily in a simple example like this), and the compiler will optimize it anyway so it really doesnt matter, I pick the clearer example (or the less typing example), but "speed" never plays a factor in how i decide.

Also, i use parenthesis around everything besides basic math operators +/*/ (and some unary operators), because I can never remember the order of operations of the others. This goes especially for stuff like dereference, address of, and &&, ||. If I mix &&/||, i ALWAYS use parenthesis to clarify what I mean.

*parray[4] = no
(*parray)[4] = yes
*(arrayp[4]) = yes
although i will do stuff like a + *pb * c

I just want to make sure I never get screwed by order of operations, especially if I cant remember them

also * sticks the variables together, + puts a space

a + b*c - d + e/f + g*h*i
(i had a professor who insisted on spaces between everything, but that just looks unclear most of the time.
a + b * c - d + e / f + g * h * i just looks ugly and i'm forced to add parenthesis in my head when reading it, unlike my way)
« Last Edit: March 05, 2010, 04:38:49 PM by Glaiel-Gamer » Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #113 on: March 05, 2010, 05:44:56 PM »

Code:
typedef enum _EBlendMode
{
   BM_MIN = -1,
   BM_ADD,
   BM_SUBTRACT,
   BM_MAX
} EBlendMode;

That typedef enum construct is a C thing, and is totally unnecessary in C++.  It's even a little dangerous, since you create two global names for the enum _EBlendMode (leading underscore followed by capital is also reserved for the compiler, watch this one), and EBlendMode.  This doesn't happen in C, since _EBlendMode goes into something called the "tag namespace" and needs to preceded by enum in order to access it.  Unless you need to include this header in a C program, this is actually a harmful construct in C++.
Ah! Habit I picked up from my school's coding standards. I missed where we were told it was a good idea so I can't remember what the tutor's reasoning was. I'll stop doing this.

There's a perfectly valid reason for doing it in C, so if you were taught that first, it may be where you got it from.  A lot people migrate from C to C++ and don't realize that they don't have to do that anymore.

The only valid reason for doing it in C++ is in the case where you're writing a header file that has to be used in both C and C++ (or Objective-C) and you don't want to keep saying "enum this" and "struct that"

Quote
Quote
I typedefine f32 as float, s32 as signed int, etc. It's useful for remembering how big everything is and it's useful in calculations.

float isn't necessarily 32 bits, nor is signed int.  This is a very misleading convention.

Which is exactly why I do this! if I'm using a system where that's not the case, I can change the types.h typedefs and all my code still (presumably) works.

I don't know of any types that are guaranteed to be a specific size so this method works for me.

Check out stdint.h.  This header is part of C, and not C++, but most C++ compilers supply it.  It should be standard with the new C++ revision, and the name will likely be changed to cstdint.  Most compilers also have fixed size types as an extension, such the aforementioned __int32, which is supported by at least Microsoft and Borland's compilers.  Note the two underscores at the beginning, this is why I warn about violating the compiler's namespace, you can clash with extensions, among other things.

Quote
Quote
I always use ++i in loops as it is slightly faster.

It's only faster if i is of some type that defines both operator ++s and the postfix ++ has to do more work than the prefix one. Many iterator types fit this description, but not all.  For the built-in types, it isn't any faster.  I'm not saything there's anything wrong with doing that, but your argument is bogus.
Really? Dang, everything I've been taught is a lie!  Tongue I was still curious about this topic so I've searched a bunch of other topics covering it a bit more thoroughly (but gave up as my internet is currently going dial-up speed and I'm impatient).

I'm still going to stick with the prefix ++ as it is tidier code, in my opinion. The postfix ++ should be used in very specific circumstances due to its nature.

For most types, ++i will never be slower than i++, but it isn't necessarily faster.  Someone could always define some type with those operators that does something totally weird in the prefix and not the postfix one, but in general this rule holds.

This comes from many of the standard library iterator types, which are often (but not necessarily) instances of some object the overload both ++ operators.  For those, ++i is usually faster.  I do know that for awhile Borland C++'s std::vector iterators were just raw pointers, so that is one case where the performance is equal between the ++s, even with iterators.

Quote
It's always interesting reading your code related posts, Average, where did you get your knowledge pool from?

Way too much experience, and I studied language and compiler design in college.  I'm actually trying to design a language right now, based entirely on generics.  I look for all these little details so I know what to avoid/embrace.
Logged



What would John Carmack do?
Sos
Level 8
***


I make bad games


View Profile WWW
« Reply #114 on: March 06, 2010, 07:01:19 PM »

I always capitalise structs. I don't know if it's a good habit, but i can tell what's struct, and should have a pointer,and what's not and should be plain ol var.

Code:
typedef struct HDLIGHT
{
  int x,y,lit;
  float sine,phase,wave,light,range,theta,d,dx,dy;
} HDLIGHT;
Logged

skyy
Level 2
**


[ SkyWhy ]


View Profile
« Reply #115 on: March 07, 2010, 04:04:27 AM »

I always capitalise structs. I don't know if it's a good habit, but i can tell what's struct, and should have a pointer,and what's not and should be plain ol var.

Code:
typedef struct HDLIGHT
{
  int x,y,lit;
  float sine,phase,wave,light,range,theta,d,dx,dy;
} HDLIGHT;

Out of sheer curiosity, why do you use typedef in this situation (HDLIGHT -> HDLIGHT)?
Logged

Sos
Level 8
***


I make bad games


View Profile WWW
« Reply #116 on: March 07, 2010, 04:36:15 AM »

Out of sheer curiosity, why do you use typedef in this situation (HDLIGHT -> HDLIGHT)?
That's my typedef, that's my style Tongue
Logged

increpare
Guest
« Reply #117 on: March 07, 2010, 04:49:20 AM »

Out of sheer curiosity, why do you use typedef in this situation (HDLIGHT -> HDLIGHT)?
It's not HDLIGHT->HDLIGHT, it's struct HDLIGHT->HDLIGHT
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #118 on: March 07, 2010, 06:16:13 AM »

I always capitalise structs. I don't know if it's a good habit, but i can tell what's struct, and should have a pointer,and what's not and should be plain ol var.

Code:
typedef struct HDLIGHT
{
  int x,y,lit;
  float sine,phase,wave,light,range,theta,d,dx,dy;
} HDLIGHT;

I believe this style originated with the Windows API, and like almost every coding convention used by that API, Microsoft has admitted that it was a terrible idea.  This one in particular is a bad idea because of (once again) macro clashes.
Logged



What would John Carmack do?
Crimsontide
Level 5
*****


View Profile
« Reply #119 on: March 07, 2010, 07:10:01 AM »

What Every Computer Scientist Should Know About Floating Point Numbers

Personally, it's not okay to run a state machine in such a fashion.

It's fucking stupid. A sub-state should be a different state variable. Using floating point numbers for states betrays a misunderstanding of how floating point variables are used in a computer and educates every programmer who maintains that code to be equally stupid.

I completely agree (warning rant incoming).

My biggest beef with C++ is actually not its high level constructs and how they fit together, but rather its low level ones.

C++ is a system programming orientated language and supposed to give you direct access in a low level way.  You can cast one pointer to another, or even one unrelated struct/class to another, modify values through unions, store pointers to ints and do all sorts of unholy transmogrifications to them and then store them back as pointers.

And yet something as simple as:
Code:
int a = 1000000;
is implementation dependent.  Is a = 1000000 or 16960?  It all depends on the underlying register size, both values are legal results for a 'compliant' C++ program.  This all starts to become important when doing shifts and masks/ect...  Or how bout this one:
Code:
int a = -2; a >>= 1;
Again, who knows what you're gonna get.  Since right shift isn't defined as either logical (a = some large positive number dependent on the register size) or arithmetic (a = -1).  Padding, alignment, endianess, and casting are all compiler dependent.

There are pages and pages in the ARM on silly little points of the finer nuances of C++ and how programs should be constructed so that they are compiler/platform independent and yet its fundamental types and operators are completely unportable.

I actually had to go through and write my own set of low level types/operators.  I can now write:
Code:
uint32Be = 0x75000001;
and know I got a 32 bit, 2's complement, big endian, 4 bytes aligned number.  I can use them in structs and know the padding/alignment of the struct, so I can serialize these structs by simply writing:
Code:
SendData(&struct,sizeof(Struct));
I can run hashes over them and compare them to hashes on other systems.  I can cast, shift, compare, and do all the low level things you would expect to be able to do, and know that if it compiles it will run the same regardless of compiler or platform it runs on.

Ehh... I've gone a bit off topic, and I still like and use C++.  I just find it a bit disheartening that such a large industry, at its core, has such silly/poorly thought out foundations.
Logged
Pages: 1 ... 4 5 [6] 7
Print
Jump to:  

Theme orange-lt created by panic