March 2025

Improving Code Safety in C++26: Managers and Dangling References -- Bartlomiej Filipek

codesafety2-filipek.pngIn this blog post, we’ll explore ways to improve the safety of a simple configuration manager. We’ll handle common pitfalls like dangling references and excessive stack usage. Additionally, we’ll see how C++26 helps enforce safer coding practices with stricter diagnostics and improved handling of large objects.

Improving Code Safety in C++26: Managers and Dangling References

by Bartlomiej Filipek

From the article:

Step 1: The Buggy Implementation 
 
Below is a simple example of a manager object that stores various configs in a map and provides a method to retrieve them. When a requested configuration isn’t found, the code attempts to return a default certificate:

codesafety-filipek.png

Do you see a potential error in this code?

. . .

At first glance, the code looks harmless. However, if the requested entry isn’t found, the function returns a reference to a temporary std::vector. Once the function exits, that temporary is destroyed—leaving you with a dangling reference and undefined behavior.

Inside STL: Waiting for a std::atomic to change, part 2 -- Raymond Chen

RaymondChen_5in-150x150.jpgLast time, we looked at how the Microsoft C++ standard library implements wait and notify_* for std::atomic<std::shared_ptr<T>>. Today, we’ll look at the other library that (as of this writing) implements std::atomic<std::shared_ptr<T>>: libstdc++.

Inside STL: Waiting for a std::atomic<std::shared_ptr<T>> to change, part 2

by Raymond Chen

From the article:

The first thing to note is that the traditional “wait for a value to change” mechanism on unix is the futex, but futexes (futexen?) are limited to 4-byte values, which is insufficient for a 64-bit pointer, much less the two pointers inside a shared_ptr.

At this point, I will refer you to learn about how libstdc++ implements waits on atomic values, particularly the section on how it handles types that do not fit in a __platform_wait_t. The remainder of this discussion will treat that as an already-solved problem and focus on the shared pointer part.

Okay, back to atomic<shared_ptr<T>>::wait():

// atomic<shared_ptr<T>>::wait
void
wait(value_type __old,
     memory_order __o = memory_order_seq_cst) const noexcept
{
    _M_impl.wait(std::move(__old), __o);
}

When you wait on a shared_ptr, the work is done by _Sp_atomic::wait:

// _Sp_atomic<shared_ptr<T>>::wait
void
wait(value_type __old, memory_order __o) const noexcept
{
    auto __pi = _M_refcount.lock(memory_order_acquire);
    if (_M_ptr == __old._M_ptr && __pi == __old._M_refcount._M_pi)
      _M_refcount._M_wait_unlock(__o);
    else
      _M_refcount.unlock(memory_order_relaxed);
}

Workshops for Everyone -- ACCU Conference

The ACCU conference has a range of workshops, including online options, this year:

Workshops for Everyone

by ACCU Conference

From the article:

See all the offerings, from Jason Turner, Nicolai Josuttis, Robert Seacord, Gail Ollis, Mateusz Pusz, Phil Nash and Mike Shah.

Making C++ Safer -- Greg Law

Should you start new projects in C++, these days? Is language safety an issue for you? What can be done, today?

Making C++ Safer

by Greg Law

From the article:

I believe that over time C++ will become a lot safer, maybe even some kind of ‘safe’. Competition is good: Clang was the best thing to happen to GCC, and Rust might turn out to be the best thing to happen to C++. That journey has already begun, with proposals for the evolution of the language including Contracts and Profiles, and simply changing some of the defaults in C++26. While the language custodians work to make the language itself safer, what can you do today?

 

Inside STL: Waiting for a std::atomic to change, part 1 -- Raymond Chen

RaymondChen_5in-150x150.jpgWhen using std::atomic<std::shared_ptr<T>>, the C++ standard defines a "change" as a modification to either the stored pointer or the control block pointer. However, since atomic wait mechanisms typically track only a single memory address, the Microsoft implementation handles this limitation by using a timeout-based polling strategy to detect changes in the control block.

Inside STL: Waiting for a std::atomic<std::shared_ptr<T>> to change, part 1

by Raymond Chen

From the article:

Like other std::atomic specializations, std::atomic<std::shared_ptr<T>> supports the wait and notify_* methods for waiting for the value to change and reporting that the value has changed. The definition of “changed” in the C++ language specification is that the value has changed if either the stored pointer or the control block pointer has changed. A shared pointer is implemented as a pair of pointers, but Wait­On­Address can wait on at most 8 bytes, and unix futexes can wait on only four bytes, so how does this work?¹

The Microsoft implementation waits for the stored pointer to change, and the notify_* methods signal the stored pointer. But wait, this fails to detect the case where the stored pointer stays the same and only the control block changes.

std::atomic<std::shared_ptr<int>> p =
    std::make_shared<int>(42);

void change_control_block()
{
    auto old = p.load();
    auto empty = std::shared_ptr<int>();

    // Replace with an indulgent shared pointer
    // with the same stored pointer.
    p.store({ empty, old.get() });
    p.notify_all();
}

void wait_for_change()
{
    auto old = p.load();
    p.wait(old);
}