Lightning Round

Lightning Round

06 Aug 2018    

Pre-increment vs Post-increment

When writing a loop, or otherwise, you can increment something by specifying either ++i or i++. While in most cases it doesn’t matter which one you use, prefer to use the pre-increment ++i operator if you can. This is because, the type that is being incremented or decremented can actually cost you expensive copies if using a post increment. While this isn’t true in most cases it’s always good to keep in mind.

Inline Variables in Control Statements

There are cases when you would assign the output of a function to a variable and then verify it in a subsequent if statement. In such cases, it is beneficial to further reduce the scope of that variable by enclosing that check within the if statement itself.

auto const currentRow = GetCurrentRow();
if (currentRow) {
    currentRow->MarkRowDirty();
}

// Instead the scope of currentRow can be reduced

if (auto const currentRow = GetCurrentRow()) {
    currentRow->MarkRowDirty();
}

In C++ 17, this is a lot easier since an initalizer is allowed in an if statement (similar to a for loop). This allows further processing and checking:

// In C++ 17
if (auto const currentRow = GetCurrentRow();
        currentRow && currentRow->HasValidIdField()) {
    currentRow->MarkRowDirty();
}

Safeguarding Unions

Unions are a way for two things to share the same storage space. While this is pretty helpful in a lot of cases, using unions in general can lead to some nasty undefined behaviour (strict aliasing violations) which isn’t visible unless you look at the generated code. Using anonymous unions along with a flag can help alleviate this behaviour. Eg this is okay:

// Inside a class declaration

union {
    double d;
    int i;
};

enum class Type {
    Double, Int
};

Type t;

// ... Some other function ...

if (t == Type::Int) return i;

// ... Some other function ...

if (t == Type::Double) return d;

Only Use static for Storage Persistence

In a lot of cases you don’t need to use the static keyword. If you have a function in a cpp file that you want to define as static it is better to put it into an anonymous namespace. This also helps inform the compiler that the function will not leak from the translation unit.

Often when creating local const variables, one might also prepend static to them. This is again not required, const is sufficient, especially for fundamental types. Unless your static const variable is being initialised using some data and has to hold onto that data for the entirety of the program, leave out static.

void SomeFunction() {}
    // static is unnecessary
    static auto const max_tries = 1U;
    // ...
}

In C++ 17, you can stop specifying static with constexpr member variables since it is implicitly inline.

= delete Anything

Any function can be marked as deleted, it doesn’t have to be a special member function or even a member function at all. Eg:

class Foo {
public:
int Add(int x, int y);
int Add(short int x, short int y) = delete; // Works!
};

// Perfectly valid and acceptible syntax
void operator+(Foo const&, Foo const&) = delete;

This is a nice way to constraint an API to avoid misuse and avoid using complicated SFINAE!

Assert Liberally

Assertions are a good way to document expected behaviour. They allow easily catching bugs during development and reduces potential problems in the future. Additionally, an assert should typically always be accompanied by a message for full clarity. So if you hit an assert in a part of the codebase you’re not familiar with, a message is very helpful in determining what might’ve caused the problem.

auto& LockLayer(std::size_t index) {
    assert(index < Collection.size()); // Good
    assert(index < Collection.size() && "Layer index does not exist"); // Better!
}

Asserts are also useful to check pre and post conditions. These are especially useful in validating the control flow in a function.

auto const attributes = GetRowAttributes();

assert(attributes.size() > 0 && "There are no attributes to process");

auto const localAttributes = ConvertAttributes(attributes);

// Contrived example
assert(localAttributes.size() > 0 && "Did not convert all attributes");

Avoid Using volatile & inline

volatile doesn’t do what you think it does, it’s not thread safe. It’s only use now is to stop the compiler from optimising the thing that was declared volatile.

Although inline is useful for templates, using this keyword with modern compilers is really not going to make much of a difference. If you actually want to use it, go for a cross platform implementation of something like forceinline instead.