21 Nov 2023 • C++ tricks: STL-free source location

C++20's source_location is dogshit:

> cat a.cpp
#include <source_location>
> cl.exe /std:c++20 a.cpp /P
Microsoft (R) C/C++ Optimizing Compiler Version 19.36.32534 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

a.cpp
> wc -l a.i
6736 a.i
> g++ -E a.cpp -std=c++20 | wc -l
99
> clang++ -E a.cpp -std=c++20 | wc -l
460

but the compilers were kind enough to all use the same intrinsics, so the DIY implementation is trivial:

// zero headers required!
struct SourceLocation {
    const char * file;
    int line;
    const char * function;
};

constexpr SourceLocation CurrentSourceLocation( const char * file_ = __builtin_FILE(), int line_ = __builtin_LINE(), const char * function_ = __builtin_FUNCTION() ) {
    return {
        .file = file_,
        .line = line_,
        .function = function_,
    };
}

Now you can throw out your old allocation macros:

// before
#define ALLOC( a, T ) ( ( T * ) ( a )->allocate( sizeof( T ), alignof( T ), __PRETTY_FUNCTION__, __FILE__, __LINE__ ) )

// after
template< typename T >
T * Alloc( Allocator * a, SourceLocation src = CurrentSourceLocation() ) {
       return ( T * ) a->allocate( sizeof( T ), alignof( T ), src );
}

and also update helper functions to have useful source info:

template< typename T >
Span< T > CloneSpan( Allocator * a, Span< T > span, SourceLocation src = CurrentSourceLocation() ) {
    Span< T > copy = AllocSpan< T >( a, span.n, src );
    memcpy( copy.ptr, span.ptr, span.num_bytes() );
    return copy;
}

// const variant because templates don't do implicit casts
template< typename T >
Span< T > CloneSpan( Allocator * a, Span< const T > span, SourceLocation src = CurrentSourceLocation() ) {
    Span< T > copy = AllocSpan< T >( a, span.n, src );
    memcpy( copy.ptr, span.ptr, span.num_bytes() );
    return copy;
}