30 Nov 2023 • C++ tricks: STL-free initializer list

Credit for this one to https://nitter.net/Donzanoid/status/1611315409596071936.

C++'s initializer_list is dogshit:

> cat a.cpp
#include <initializer_list>
> cl.exe /std:c++20 a.cpp /P > /dev/null; and wc -l a.i
Microsoft (R) C/C++ Optimizing Compiler Version 19.36.32534 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

a.cpp
8988 a.i
> g++ -E a.cpp -std=c++20 | wc -l
115
> clang++ -E a.cpp -std=c++20 | wc -l
393

There's actually no compiler magic involved here and initializer_list is pretty much just span so very simple to implement, but you need a few tricks to get it to actually work:

On that last point you may be wondering why bother with this at all. You can't keep a codebase completely STL-free, be it because of certain things where the non-STL way is extremely painful (e.g. MSVC atomics), or unavoidable third party libraries pulling in STL headers (e.g. metal-cpp). So we need to make this not clash with the real thing.

#if COMPILER_GCC

// GCC refuses to compile if the implementation doesn't exactly match the STL
#include <initializer_list>

#else

#define _INITIALIZER_LIST_ // MSVC
#define _LIBCPP_INITIALIZER_LIST // clang

namespace std {
    template< typename T >
    class initializer_list {
    public:

#if COMPILER_MSVC
        constexpr initializer_list( const T * first_, const T * one_after_end_ ) : first( first_ ), one_after_end( one_after_end_ ) { }
#else
        constexpr initializer_list( const T * first_, size_t n ) : first( first_ ), one_after_end( first_ + n ) { }
#endif

        const T * begin() const { return first; }
        const T * end() const { return one_after_end; }
        size_t size() const { return one_after_end - first; }

    private:
        const T * first;
        const T * one_after_end;
    };
}

#endif

You may also want to add your own header guards against the STL defines, or you can just make sure you always include this first.