Thursday 23 October 2014

C++ Correct multi threaded singleton initialisation

Correct double-checked locking pattern

1. Pointer must be atomic
2. Check, lock, check, construct

std::atomic<Foo*> foo { nullptr };

Foo* instance()
{
    Foo* f = foo; // single load of foo
    if (!f)
    {
        std::lock_guard<std::mutex> l(foo_lock);
        if (!foo)
        {
            foo = f = new Foo(); // assign both foo and f
        }
    }
    return f;
}

Even better, use std::unique_ptr and std::once to get automatic cleanup and less scaffolding

class Foo
{
public:
    static Foo& instance()
    {
        std::call_once(_create, [=]{
            _instance = std::make_unique<Foo>();
        });
        return *_instance;
    }

private:
    static std::unique_ptr<Foo> _instance;
    static std::once_flag       _create;
};

Or just use a function local static

Foo& Foo::instance()
{
    static Foo foo;
    return foo;
}

C++ decltype and auto type deduction

auto type deduction strips const, volatile and ref

const int& bar = foo;
auto baz = bar; // strips const and ref - therefore type of baz is int

decltype type deduction doesn't strip const, volatile and ref

// decltype of a name

const int& bar = foo;
decltype(bar) // does not strip const and ref - therefore type is const int&

// decltype of an expression
decltype(lvalue expression) always returns an lvalue reference

int arr[5];
arr[0] = 5;
decltype(arr[0]) // lvalue reference, therefore type is int&



C++ type information at run time

std::type_info::name and typeid(T).name() will give incorrect results, as required by the standard

use Boost.TypeIndex

#include <boost/type_index.hpp>

boost::type_index::type_id_with_cvr<T>().pretty_name();
boost::type_index::type_id_with_cvr<decltype(t)>().pretty_name();

C++14 mutable lambda and by-value and by-value init capture

by-value capture vs by-value init capture

by-value capture: type of `i` is `const int`
{
const int i = 0;
auto lambda = [i]() { };
}

by-value init capture: type of `i` is `int`
{
const int i = 0;
auto lambda = [i=i]() { }; 
}

lambda function call operator is const

error: by-value capture: type of `i` is `int`, but default lambda operator() is const member function
{
int i = 0;
auto lambda = [i]() { i = 1; }; 
}

error: by-value init capture: type of `i` is `int`, but default lambda operator() is const member function
{
const int i = 0;
auto lambda = [i=i]() { i = 1; }; 
}

making lambda function call operator mutable

error: by-value capture: type of `i` is `const int`, can't assign, even though lambda operator() is mutable member function
{
const int i = 0;
auto lambda = [i]() mutable { i = 1; };  
}

by-value capture: type of `i` is `int`, and lambda operator() is mutable member function
{
int i = 0;
auto lambda = [i]() mutable { i = 1; };  
}

by-value init capture: type of `i` is `int`, and lambda operator() is mutable member function
{
const int i = 0;
auto lambda = [i=i]() mutable { i = 1; }; 
}