fastest 3D software for linux

Linux 3D
interactive ray-tracing
[Introduction] [Overview] [Coding Standards] [Core reference] [3D reference] [GUI]

Equinox3D C coding standards

Contents

  • Indenting
  • Names
  • Variables
  • Pointers
  • Prefixes
  • Switch / case
  • Debug-code
  • Conditional compilation


  • Indenting

    Use tabs for indenting:
  • Some folks like large indenting steps, some of us prefer small steps. Using tabs allows everyone to change the appearance locally, without modifying the file.
  • The ASCII standard has a dedicated character for indenting (tab). Use it!
  • It leads to smaller files.

  • After the first non-whitespace character, use spaces for aligning, so tab-size changes move things smoothly.
    Tabs before "int" and "float", but spaces after them.
      int    LVar = 0;
      float  LNumber = 42.0;
    
    Put the opening bracket for "if", "for", "while" etc. in a separate line, for better readability:
      if(LMesh0 == LMesh1)
      {
        if(LThis > LThat)
        {
        }
        else if(LThis < LThat)
        {
        }
        else
        {
        }
      }
    
      EIndex  LI = 0;for(;LI < LN;LI++)
      {
      }
    

    Names

    Use upper-camel case names for types, variables and functions, as it allows for consistency.
    Example:
    E3dMesh_PolyGroupAdd()
    This guarantees that the 'M' in 'Mesh' is always capitalized, allowing for case-sensitive searches.

    Avoid using lower camel-case because it creates a mess.
    It would make the 'm' in 'Mesh' sometimes upper case, sometimes lower case:
    Example:
    startMeshIterate()  // 'Mesh' is capitalized
    meshIterate()       // And, now, it isn't. Can't do a case-sensitive search!
    

    Variables

    Practice good variable 'hygene':
  • Declare variables at the tightest-possible scope to avoid side-effects.
  • Use 'const' generously.
  • // We 'guarantee' that PMesh and PCount will not be changed by this function (multi-threading friendly)
    static void _SomeFunc(const E3dMesh* PMesh, const EIndex PCount)
    {
      if(PCount > 0)
      {
        int  LX = 0;  // Define here, not at the top of the function, so other branches can't see it
    ...
      }
      else
      {
      }
    }
    

    Pointers

    Pointer types should have the '*' right after the base type because it is part of the type.
    Proof: You can do this in C, so the '*' is clearly part of the type:
    typedef int*  IntPtr;
    Good:
    int*  LVar0 = NULL;int*  LVar1 = NULL;
    
    typedef int*  IntPtr;
    IntPtr  LVar0 = NULL, LVar1 = NULL;
    Bad / confusing:
    int  *LVar0 = NULL, *LVar1 = NULL, LVar2 = 7;
    We consider the C standard allowing this, as a "bug".


    Prefixes

    Prefixes are used for name spaces to make global searches easy.
    For example:
    E3d library types look like this:
    E3dMesh, E3dPolyGroup
    Image ("picture") library types use the Ep prefix:
    EpImage, EpPixelBuffer, EpToneMapper
    Types are used as function name prefixes, before an "_'.
    Examples:
    E3dMesh_PolyGroupAdd(E3dMesh* PMesh)
    EpImage_Resize(EpImage* PImage, ...)
    This makes things familiar to C++ folks:
    E3dMesh::PolyGroupAdd()
    EpImage::Resize()
    Function parameters are prefixed with a 'P' and local variables in functions are prefixed with an 'L'.
    This makes it easy to tell them apart with a quick glance:
    void E3dEdge* E3dMesh_EdgesAdd(E3dMesh* PMesh, const EIndex PCount, const EBool PInitialize)
    {
      E3dEdge*  LNewEdges = NULL;
      EIndex  LI = 0;for(;LI < LN;LI++)
      {
        if(PMesh->Vertices.Count > 0)  // I can immediately tell that PMesh is a function parameter and not a local or a global variable
        {
        }
    ...
      }
      return(LNewEdges);
    }
    
    Macro function parameters and local variables are prefixed with a lower-case "p" a lower-case 'l', respectively. This helps avoid clashes with parameters and local varialbles of "real" functions.
    The use of "do" and "while(0)" are encouraged for macros, as the "while(0)" "swallows" the semicolon when calling the macro, so macro function calls are consistent with regular function calls:
    #define E3dMesh_SomeMacro(pOldMesh) do\
    {\
      int  lSomeCounter = 0;\
      E3dMesh*  lNewMesh = NULL;\
    } while(0)
    
    ...
    
    E3dMesh*  LMesh = LSomeMesh;
    E3dMesh_SomeMacroFunc(LMesh);   // <-- ';' is swallowed by the 'while(0)' in the definition of E3dMesh_SomeMacroFunc
    
    Global variables should be avoided, if possible, but if used, they should have some namespace prefix, to minimize clashes. Example:
    EIndex  E3d_MaxNumOfSomething = 42;
    Static global variables and function names should start with an '_'
    static int  _SomeVariable = 42;
    static void _SomeLocalFunction(void);
    

    Comments

    Do not indent comments. This allows for more room without breaking into several lines when inside a deep inner scope.
      if()
      {
         if()
         {
           for()
           {
    // Starting the comment here leaves more room and it's easier on the eye for reading through multiple comments.
             // Versus starting here.
             if()
             {
               // Then here. Now, your eye has to jump horizontally from comment to comment...
               if()
               {
               }
             }
           }
         }
      }
    

    Loops

    Define and initialize loop variables before the 'for' statement. This allows for multiple types:
      {
    // All loop variables are declared and initialized right here:
        EIndex  LI = 0;E3dVertex*  LSrcVertex = LMesh->Vertices.Items;E3dCoord3*  LPosition = LPositions;for(;LI < LN;LI++)
        {
        }
      }
    
    C allows for this, but it's a half-baked solution because it only allows for one type ('int', in this case) and often, multiple types are needed.
    for(int LI = 0:LI < 3;LI)

    Switch / case

    Format switch / case statements like this for good readability:
      switch{LVariable)
      {
        case 0:
          {  // Need a '{' because we are declaring variables
            int  LBlah = 72;
    ...
          }
        break;
    
        case 1:
    ...
        return(42);
    
        default:  // Always hava a 'default'
        break;
      }
    

    Debug-code

    Temporary debug-code is allowed. It should not be indented, so we can easily identify it. Use conditionals to keep console output noise-free.
    if(LMesh0 == LMesh1)
    {
      if(LThis > LThat)
      {
      }
    // Debug
    if(E3dDebugSOME_CRITERIA) { printf("%s() %s:%d  %sDebug printf%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout); }
      }
    }
    

    Conditional compilation

    When possible, use 'real' conditionals, even when using #define for conditional compilation. This ensures that the code path is parsed by the compiler, which helps to prevent "code rot":
    #define E3dPROTOTYPE_MESH_CODE  0
    ...
    
    Good:
      if(E3dPROTOTYPE_MESH_CODE)
      {
    ...
      }
    
    Bad, allows for code rot:
    
    #if E3dPROTOTYPE_MESH_CODE
    ...
    #endig
    
    The compiler will not generate machine code for either case, but in the first case, it will parse the inner code, so it will guarantee that the unused code will always compile.


    © 1996-2025 By Gabor Nagy