Ch6.10: fast_io views

Why this chapter exists

In the previous chapter, you learned that C-style array parameters and pointer+length pairs are error-prone:

Modern C++ solves this with view types: lightweight, non-owning objects that refer to existing memory without copying it.

The C++ standard library provides std::span and std::string_view, but on Windows x86‑64, std::span suffers from a calling‑convention limitation:

This ABI behavior was discovered by the author of this tutorial and reported to Microsoft. Until the ABI is fixed, std::span is less efficient on Windows x86‑64 than a simple (ptr, n) pair.

The wincall proposal

To address these long-standing ABI inefficiencies, the author proposed a new calling convention named wincall, designed for x86‑64 Windows under Intel APX. It is documented here:

Microsoft Developer Community: wincall proposal

and discussed here:

GCC mailing list discussion

The motivation for wincall includes:

wincall aims to:

This chapter does not depend on wincall, but understanding the ABI limitations explains why std::span is inefficient on Windows today.

Where fast_io fits in

The fast_io library does not “fix” the Windows ABI. Instead:

In this chapter, you will learn how to use the following fast_io view types:

These types are safer than raw pointers and avoid the Windows ABI inefficiencies that affect std::span.

1. ::fast_io::span<T>

::fast_io::span<T> is a lightweight, non-owning view over a contiguous sequence of T. It stores:

Unlike std::span, it is intentionally sized to avoid the Windows x86‑64 16‑byte penalty. Because fast_io is not ABI-stable, it can choose a representation that is efficiently passed in registers on all major platforms.

Example: passing a span


void process(::fast_io::span<int const> s)
{
    for(auto e : s)
        ::fast_io::io::println(e);
}

Example: creating a span from a vector


::fast_io::vector v{1,2,3,4};
process(::fast_io::span{v});

Example: creating a span from a raw array


int arr[4]{1,2,3,4};
process(::fast_io::span{arr});

2. ::fast_io::string_view

::fast_io::string_view is similar to std::string_view, but designed to be ABI-friendly on all major platforms, including Windows x86‑64. It:

Example


void greet(::fast_io::string_view name)
{
    ::fast_io::println("Hello, ", name);
}

3. ::fast_io::cstring_view

::fast_io::cstring_view is a view over a null-terminated C string. It stores only a pointer and computes the length when needed.

This is ideal for interfacing with C APIs or legacy code.

Example


void debug(::fast_io::cstring_view s)
{
    ::fast_io::io::println("debug: ", s);
}

4. ::fast_io::index_span<T, N>

::fast_io::index_span<T, N> is a fixed-size view over exactly N elements. It:

Example


void print_slice(::fast_io::index_span<std::size_t, 5> idx)
{
    for(std::size_t i{}, n{idx.size()}; i != n; ++i)
        ::fast_io::io::println(idx[i]);
}

5. Performance comparison

The key difference between std::span and ::fast_io::span is how they behave under different ABIs.

Type Size Passed in registers (SysV) Passed in registers (Win64) Notes
T* 8 bytes yes yes baseline pointer semantics
(T*, std::size_t) 16 bytes yes no passed via memory on Win64
std::span<T> 16 bytes yes no same penalty as (ptr, n)
::fast_io::span<T> 8 bytes yes yes avoids Win64 memory‑passing penalty

::fast_io::span is intentionally sized to behave like a raw pointer at the ABI level, ensuring efficient register passing on all major platforms.

6. When to use each type

Key takeaways