Here is my Christmas present to you this year, some of my favorite coding tips:
Use plenty of whitespace. Do you prefer tabs or spaces? Who cares, just make sure you use plenty of whitespace, especially newlines in your code. The motivation is making the code easier to read. Consider the following example from
Fridge:
void Panel::Render()
{
if( !m_bVisible )
{
return;
}
if( m_xImage != INVALID_STRING_HASH )
{
int iTextureID = TextureManager::GetTextureID( m_xImage );
glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
glBindTexture( GL_TEXTURE_2D, iTextureID );
}
else
{
glBindTexture( GL_TEXTURE_2D, INVALID_TEXTURE_ID );
u_int uSwapped = MathsHelper::RGBAtoABGR( m_uColour );
glColor4ubv( reinterpret_cast<GLubyte*>( &uSwapped ) );
}
glBegin( GL_QUADS );
glTexCoord2f( 0.0f, 0.0f );
glVertex2i( GetResizedX(), GetResizedY() );
glTexCoord2f( 0.0f, 1.0f );
glVertex2i( GetResizedX(), GetResizedY() + GetResizedHeight() );
glTexCoord2f( 1.0f, 1.0f );
glVertex2i( GetResizedX() + GetResizedWidth(), GetResizedY() + GetResizedHeight() );
glTexCoord2f( 1.0f, 0.0f );
glVertex2i( GetResizedX() + GetResizedWidth(), GetResizedY() );
glEnd();
if( m_bBorder )
{
const float fPositionX = static_cast< float >( GetResizedX() );
const float fPositionY = static_cast< float >( GetResizedY() );
const float fWidth = static_cast< float >( GetResizedWidth() );
const float fHeight = static_cast< float >( GetResizedHeight() );
glBindTexture( GL_TEXTURE_2D, INVALID_TEXTURE_ID );
u_int uSwapped = MathsHelper::RGBAtoABGR( uPANEL_BORDER_COLOUR );
glColor4ubv( reinterpret_cast<GLubyte*>( &uSwapped ) );
glBegin( GL_LINE_LOOP );
// Top
// glVertex2f( fPositionX, fPositionY );
glVertex2f( fPositionX + fWidth, fPositionY );
// Right
// glVertex2f( fPositionX + fWidth, fPositionY );
glVertex2f( fPositionX + fWidth, fPositionY + fHeight );
// Bottom
// glVertex2f( fPositionX + fWidth , fPositionY + fHeight );
glVertex2f( fPositionX, fPositionY + fHeight );
// Left
// glVertex2f( fPositionX, fPositionY + fHeight );
glVertex2f( fPositionX, fPositionY );
glEnd( );
}
Widget::Render();
}
And again with more spartan use of whitespace:
if(!m_bVisible) return;
if(m_xImage!=INVALID_STRING_HASH)
{
int iTextureID=TextureManager::GetTextureID(m_xImage);
glColor4f(1.0f,1.0f,1.0f,1.0f);
glBindTexture(GL_TEXTURE_2D,iTextureID);
}
else
{
glBindTexture(GL_TEXTURE_2D,INVALID_TEXTURE_ID);
u_int uSwapped=MathsHelper::RGBAtoABGR(m_uColour);
glColor4ubv(reinterpret_cast<GLubyte*>(&uSwapped));
}
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f);
glVertex2i(GetResizedX(),GetResizedY());
glTexCoord2f(0.0f,1.0f);
glVertex2i(GetResizedX(),GetResizedY()+GetResizedHeight());
glTexCoord2f(1.0f,1.0f);
glVertex2i(GetResizedX()+GetResizedWidth(),GetResizedY()+GetResizedHeight());
glTexCoord2f(1.0f,0.0f);
glVertex2i(GetResizedX()+GetResizedWidth(),GetResizedY());
glEnd();
if(m_bBorder)
{
const float fPositionX=static_cast<float>(GetResizedX());
const float fPositionY=static_cast<float>(GetResizedY());
const float fWidth=static_cast<float>(GetResizedWidth());
const float fHeight=static_cast<float>(GetResizedHeight());
glBindTexture(GL_TEXTURE_2D,INVALID_TEXTURE_ID);
u_int uSwapped=MathsHelper::RGBAtoABGR(uPANEL_BORDER_COLOUR);
glColor4ubv(reinterpret_cast<GLubyte*>(&uSwapped));
glBegin(GL_LINE_LOOP);
// Top
// glVertex2f(fPositionX,fPositionY);
glVertex2f(fPositionX+fWidth,fPositionY);
// Right
// glVertex2f(fPositionX+fWidth,fPositionY);
glVertex2f(fPositionX+fWidth,fPositionY+fHeight);
// Bottom
// glVertex2f(fPositionX+fWidth ,fPositionY+fHeight);
glVertex2f(fPositionX,fPositionY+fHeight);
// Left
// glVertex2f(fPositionX,fPositionY+fHeight);
glVertex2f(fPositionX,fPositionY);
glEnd();
}
Widget::Render();
In C++ this extends to using scope wherever possible, single line if or while statements can be difficult to debug in
MSVS and using scope inside of switch statements is useful since you gain the ability to declare variables local only to that scope. Without spaces and scope the code becomes a blob of characters which is inherently difficult to read - even with comments, good variable names, Hungarian notation and efficient code.
Check your pointers. If you are using C, C++ or some other language with raw memory access and pointers then initialise them to some value (0x00000000 and 0xCCCCCCCC are popular) and make sure they have been set to something else before dereferencing them. Even though I try to be religious about this I still get stung with an uninitialised pointer to debug every once in a while, and unless it breaks things horribly it can be subtle and difficult to trace. Then again, it hurts to check them mindlessly - a silent, well handled pointer failure can hide problems if some rarely used feature breaks. My usual solutions are to report such failures via
assertions, returning an error code from the function or presenting an error message to the user. If you care that much about the speed you can lose by doing all of this, you should have a seperate build configuration which will compile out any pointer checks that should be redundant along with the assert messages and the like.
Look at the assembler code. You should probably step through it too if you are getting confusing results when stepping through the source code.This one applies especially to C++ and optimisation, because its difficult to guess whether something will optimise away or not - the only way to be sure is to inspect the assembler code, it might also reveal more about the effects of compiler options, inlining and other features which are otherwise invisible when only looking at the source code - if you have inline assembler statements then you can also make sure that they are integrated with the surrounding code correctly, or are being compiled the way you want them to be. MSVS helps a lot here, interleaving your C++ code segments or inline assembler instructions with the compiled code, making it easier to interpret.
Practice. Self explanatory really.
Merry Christmas! :)