Skip to content

Coding Standard

Eoin Murphy edited this page Jan 14, 2020 · 1 revision

rez package / github repo name

Start with AL_USD then pascal case

 AL_USDProtoStuff

Maya Node Names

AL_usd_SomeNode

Maya Translator Names

AL_usd_Import
AL_usd_Export

Maya Command Names

AL_usd_SomeCommand

C++

indentation

Use 2 spaces for indentation.

Macros

#define AL_ALL_IN_CAPS(X)

datatypes

Prefer the integer types from

Any variables dealing with array lengths, or byte counts, should be size_t. I am a little loose on this definition though - in some places uint32_t may be more appropriate - e.g. Maya's array containers use 32 bit unsigned integers for their counts.

auto

auto is good. Use it wherever it makes sense within function bodies.

void goodUseOfAuto(const std::vector<int>& blah)
{
  for(auto i : blah)
  {
    std::cout << i << "\n";
  }
}

class BadUseOfAuto
{
public:

  // do not use auto for return types. It's really annoying. 
  auto getFoo()
    { return m_foo; }

private:
  float m_foo;
}; 

virtual / override

class Base
{
public:
  virtual void doStuff();
}:
class Derived : public Base
{
public:
  // always use the override specifier
  void doStuff() override;
}:

vector iteration

std::vector<int32_t> values = getFromSomewhere();

// use range based for where possible. (for constant values)
for(auto value : values)
{
  std::cout << value << std::endl;
}

// use range based for where possible. (for mutable values)
for(auto& value : values)
{
  value = 42;
}

// if that is not possible, prefer:
for(auto it = values.begin(), end = values.end(); it != end; ++it)
{
  *it = 42;
}

// and if you *really* need indices
for(size_t i = 0, num = values.size(); i != num; ++i)
{
  values[i] = i;
}

// This is bad.
for(size_t i = 0; i != values.size(); ++i)
{
  values[i] = i;
}

// This is also bad.
for(auto it = values.begin(); it != values.end(); ++it)
{
  *it = 42;
}

namespaces

AL as the first namespace then lower case only namespaces. Contents of the namespaces should not be indented.

namespace AL {
namespace inside {
namespace out {

class Foo {};

}
}
}

using namespace

Nope, Nope, Nope, Nope. Using namespace is a crime against humanity.

Classes

pascal case names

class WellNamedClass
{
public:

  WellNamedClass();

  /// \brief  member functions should start with a lower case.
  ///         Ideally they should include some doxygen docs! 
  /// \param  someVar Indicate valid values in the docs
  /// \return any info about error codes returned is useful.
  void memberFunc(int32_t someVar) const;

  /// inline functions *can* be good things, but only if they are actually very small!
  /// If the function body fits neatly on one line, it's a good inline function.
  /// If however the function body is quite large, move it to the cpp file!
  inline int32_t someVar() const
    { return m_someVar; }

private:
  // member vars should all begin with 'm_',
  int32_t m_someVar;
};

Member Variable Ordering

Member variables in classes and structs, should ALWAYS be ordered based on sizeof(type), with the biggest variables first, the smallest variables last. This has two nice little side effects:

Compiler generated padding will never be inserted into the middle of a class (only at the end if needed). Variables that require alignment will always be correctly aligned.

struct BadStruct
{
  bool m_bool;
  int32_t m_int32;
  int8_t m_int8;
  double m_double;
  int16_t m_int16;
  float m_float;
};

struct GoodStruct
{
  double m_double;
  float m_float;
  int32_t m_int32;
  int16_t m_int16;
  int8_t m_int8;
  bool m_bool;
};

// sizeof(BadStruct) == 32
// sizeof(GoodStruct) == 24

Enums

enums are good things.

If the enums are in the global scope, use 'enum class' to scope the enum values. If the enum is contained within a class, then it's probably easier to use a normal enum Bit flags cannot be 'enum class', so try to scope within a class or namespace. Specifying the base type of the enum is also good (e.g. does it use 32bits, 8bits, etc).

enum class MyEnumType : uint32_t
{ 
  kPossibleValue1, 
  kPossibleValue2, 
  kPossibleValue3
};
 
class SomeType
{ 
  enum MyEnumType : uint32_t
  { 
    kPossibleValue1, 
    kPossibleValue2, 
    kPossibleValue3
  };
};

If / Else / While / For / switch

if(condition)
{
}
else
if(condition2)
{
}
else
{
}
 
while(condition)
{
}
 
for(int i = 0, n = 10; i < n; ++i)
{
}
 
switch(value)
{
case 1:
  break;
case 2:
  {
    someCode();
  }
  break;
default:
  break;
}

When using multiple conditions within an if or while, make sure the cheapest conditions are on the left

bool enabled = false;
 
// this is bad
if(someFunctionCall() && enabled)
{
}
 
// this is better
if(enabled && someFunctionCall())
{
}

When using an if/else statement, ensure that the most likely case happens first

bool unlikelyToBeTrue = someCondition();
 
// this is bad
if(unlikelyToBeTrue)
{
  // mis-predicted branch!
}
else
{
}
 
// this is better
if(!unlikelyToBeTrue)
{
  // branch predictor says thank you!
}
else
{
}

files

sources

camel case names

WellNamedFile.h

binaries

camel case names

libWellNamed.so

header scoping

physical headers organization and namespaces have to reflect each other

#include "AL/inside/out/WellNamedFile.h"
 
namespace AL {
namespace inside {
namespace out {

class Foo 
{
};

}
}
}

python

packages

AL as the parent package then lower case only package names

AL.inside.out

classes

pascal case names

class WellNamedClass (Object):

modules

camel case names

wellNamedModule.py