WebKit and C++11

I am happy to announce that as of r155146, we now require our various ports to build with compilers that support some C++11 features. This means that we don’t need to use the COMPILER_SUPPORTS() macro to conditionally use these features anymore. These are:

  • Type inference
  • Static assertions
  • Move semantics

We’ve chosen these three features because they are well-supported in recent versions of the compilers we use: clang, MSVC and GCC.

What does this mean for people writing code? Here are three code examples where these three features come in handy:

Type Inference

Type inference using the auto keyword will automatically deduce the type of a variable based on its initializer. This is especially useful iterators. This loop:

HashMap<OriginStack, OwnPtr<ExecutionCounter> >::const_iterator end = m_counters.end();
for (HashMap<OriginStack, OwnPtr<ExecutionCounter> >::const_iterator iter = m_counters.begin(); iter != end; ++iter) {
    ...
}

Becomes:

for (auto it = m_counters.begin(), end = m_counters.end(); it != end; ++it) {
    ...
}

Unfortunately, the new range-based for syntax is not supported by all compilers, but this is definitely a step in the right direction.

Static Assertions

The new static_assert is a way to declare compile-time assertions. If an assertion is false, the compiler will produce an error. WTF already has a COMPILE_ASSERT macro that provides this functionality, but static_assert produces better error messages.

COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), atomic_string_and_string_must_be_same_size);

Gives the error:

/Source/WTF/wtf/text/AtomicString.cpp:43:1: error: 'dummyatomic_string_and_string_must_be_same_size' declared as an array with a negative size
COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), atomic_string_and_string_must_be_same_size);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Source/WTF/wtf/text/AtomicString.cpp:23:
In file included from /Source/WTF/config.h:62:
In file included from /Source/WTF/wtf/FastMalloc.h:25:
In file included from /Source/WTF/wtf/PossiblyNull.h:29:
/Source/WTF/wtf/Assertions.h:324:60: note: expanded from macro 'COMPILE_ASSERT'
#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1]

Whereas:

static_assert(sizeof(AtomicString) == sizeof(String), "AtomicString and String must have the same size");

Gives:

/Source/WTF/wtf/text/AtomicString.cpp:43:1: error: static_assert failed "AtomicString and String must have the same size"
static_assert(sizeof(AtomicString) == sizeof(String), "AtomicString and String must have the same size");
^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Move Semantics

Move semantics can provide improved performance when passing objects by value by moving the data instead of copying it. What it means for WebKit is that we can stop using out parameters in functions that return Vector. For example:

void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems)
{
    // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
    elements()->namedItems(name, namedItems);

    HTMLElement* elementFromPast = elementFromPastNamesMap(name);
    if (namedItems.size() == 1 && namedItems.first() != elementFromPast)
        addToPastNamesMap(toHTMLElement(namedItems.first().get())->asFormNamedItem(), name);
    else if (elementFromPast && namedItems.isEmpty())
        namedItems.append(elementFromPast);
}

Becomes:

Vector<RefPtr<Node>> HTMLFormElement::namedElements(const AtomicString& name)
{
    // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
    Vector<RefPtr<Node>> namedItems = elements()->namedItems(name);

    HTMLElement* elementFromPast = elementFromPastNamesMap(name);
    if (namedItems.size() == 1 && namedItems.first() != elementFromPast)
        addToPastNamesMap(toHTMLElement(namedItems.first().get())->asFormNamedItem(), name);
    else if (elementFromPast && namedItems.isEmpty())
        namedItems.append(elementFromPast);

    return namedItems;
}
Note: This may have been true in the past in some cases too, due to the named return value optimization), but now it’s safe to do this for all Vector objects with a zero inline capacity as well as HashMap and HashSet too!

Move semantics is an interesting topic that I hope to cover further in another blog post, so I’ll only mention it briefly here.

One More Thing

Astute readers may have noticed another C++11 feature in the previous example that we can now use. With C++11 there is no need to use a space between right angle brackets when closing template arguments lists! This means that:

OwnPtr<Vector<RefPtr<Node> > > m_childNodes;

Becomes:

OwnPtr<Vector<RefPtr<Node>>> m_childNodes;

Personally I’m really excited about using these features and I think they will be useful throughout the codebase. In time we’re going to start requiring even more C++11 features but this is a good start.