c++ project structure [C++ Shorts Lesson 33]π₯Mike ShahWelcome to SwedenCpp
Latest blogs, videos, podcasts and releases in one stream
Sunday, August 9, 2026
c++ project structure [C++ Shorts Lesson 33]π₯Mike ShahIf this page is useful, please consider donating a coffee
Saturday, August 8, 2026
Understanding std::counting_semaphore and std::binary_semaphore from C++20This article explains the two semaphore types introduced in C++20: std::counting_semaphore and std::binary_semaphore . Weβll first use a counting semaphore to limit how many threads can operate at the same time. Then weβll use a binary semaphore to send a signal between threads. Weβll also look at timed waiting, a small RAII helper, and a few more details. Note: The synchronization features discussed here are available in C++20. The examples use C++23 std::println for cleaner output. Letβs go. Basics A mutex works well when only one thread should enter a protected section at a time. But sometimes that limit is too strict. Imagine an application with three database connections. Running only one database operation at a time would waste two of them. On the other hand, allowing any number of threads to start an operation could overload the database. What we need is a limit: three threads may continue, while the others wait. There is another common case. One thread prepares some data, and another thread waits until the data is ready. Semaphores work well for both problems. A lot of multi-threading libraries have semaphores, but itβs pretty cool that the C++20 Standard Library now includes them right out of the box. API A counting semaphore is declared as: std::counting_semaphore The main operations are: Function Description counting_semaphore(desired) Creates a semaphore with the counter set to desired acquire() Decreases the counter, or waits if it is zero release(update) Increases the counter by update ; the default is 1 try_acquire() Tries once without waiting try_acquire_for() Waits for a limited duration try_acquire_until() Waits until a given time point max() Returns the largest counter value supported by the implementation std::binary_semaphore is an alias for: std::counting_semaphore It is useful when one outstanding signal is enough. Letβs start with the counting version. Limiting concurrency with std::counting_semaphore Suppose we have eight jobs, but only three should perform an expensive operation at the same time: #include #include #include #include // #include #include int main () { constexpr int workerCount = 8 ; constexpr int slotCount = 3 ; std :: counting_semaphore slotCount > slots { slotCount }; std :: mutex outputMutex ; int active = 0 ; auto worker = [ & ]( int id ) { slots . acquire (); { std :: lock_guard lock ( outputMutex ); ++ active ; std :: println ( "worker {} entered; active={}" , id , active ); } std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( 250 ) ); { std :: lock_guard lock ( outputMutex ); -- active ; std :: println ( "worker {} leaving; active={}" , id , active ); } slots . release (); }; std :: vector std :: jthread > threads ; threads . reserve ( workerCount ); for ( int i = 0 ; i workerCount ; ++ i ) threads . emplace_back ( worker , i ); } Run @Compiler Explorer The semaphore starts with three available slots. The first three workers call acquire() and continue. The internal counter then reaches zero. When a fourth worker calls acquire() , it has to wait. Once one of the active workers finishes and calls release() , a slot becomes available again. One waiting worker can then continue. One possible part of the output is: worker 0 entered; active=1 worker 2 entered; active=2 worker 1 entered; active=3 worker 2 leaving; active=2 worker 4 entered; active=3 The order may change between runs, but active should never be greater than three. The mutex in this example does not limit the number of workers. It only protects the diagnostic counter and keeps the output readable. The semaphore is the part that enforces the three-worker limit. Logging can slightly change thread scheduling, but it does not change the rule enforced by the semaphore. The mutex is held only for a short print operation, not for the simulated work. This is also why the code is not a traditional critical section. Three workers are allowed to run the operation together. We are limiting concurrency, not forcing complete mutual exclusion. There is one more detail: the semaphore knows how many slots are available, but it does not know what those slots represent. If they stood for three real database connections, we would still need a separate container holding those connections. Returning a slot with RAII The example above calls release() by hand. That works, but it is easy to make a mistake. An early return or an exception between acquire() and release() could prevent the slot from being returned. Other threads might then wait forever, even though the underlying work has already stopped. This is similar to calling lock() and forgetting to call unlock() . A small RAII guard can help: template std :: ptrdiff_t LeastMaxValue > class SemaphoreGuard { public : explicit SemaphoreGuard ( std :: counting_semaphore LeastMaxValue >& sem ) : sem_ ( sem ) { sem_ . acquire (); } ~ SemaphoreGuard () { sem_ . release (); } SemaphoreGuard ( const SemaphoreGuard & ) = delete ; SemaphoreGuard & operator = ( const SemaphoreGuard & ) = delete ; private : std :: counting_semaphore LeastMaxValue >& sem_ ; }; It can be used at the start of a scope: SemaphoreGuard guard { slots }; The constructor waits for a slot, and the destructor returns it. This works well when the slot represents reusable capacity: a database connection, an upload slot, a buffer, or access to limited hardware. On the other hand, it does not fit every signaling example. A one-way signal is often meant to be consumed rather than returned. Signaling with std::binary_semaphore A mutex has ownership. The thread that locks it must also unlock it. A semaphore does not have this rule. One thread can wait in acquire() , while another thread calls release() . That makes std::binary_semaphore useful for simple communication between threads. In the next example, the worker waits until the main thread tells it to start. It calculates a result and then sends a signal back: #include #include #include int main () { std :: binary_semaphore startSignal { 0 }; std :: binary_semaphore doneSignal { 0 }; int result = 0 ; std :: jthread worker ([ & ] { startSignal . acquire (); result = 42 ; doneSignal . release (); }); std :: println ( "[main] starting worker" ); startSignal . release (); doneSignal . acquire (); std :: println ( "[main] result = {}" , result ); } Run @Compiler Explorer Both semaphore counters start at zero. The worker stops at startSignal.acquire() . The main thread calls startSignal.release() , which sends the start signal and lets the worker continue. After writing result , the worker calls doneSignal.release() . The main thread waits for this signal before reading the result. The semaphore also handles memory synchronization. The main thread sees the writes made by the worker before the release() that allowed doneSignal.acquire() to finish. That means the main thread can safely read result after doneSignal.acquire() returns. The signal is not returned afterward, and that is fine. It represented a one-time notification rather than a reusable slot. Waiting with a timeout acquire() can wait forever. Some code cannot accept that. For example, an application may want to report an error, retry the operation, or switch to a fallback path after waiting for too long. try_acquire_for() waits for a relative duration: #include #include #include #include int main () { std :: counting_semaphore 1 > sem { 0 }; std :: jthread notifier ([ & ] { std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( 200 ) ); sem . release (); }); if ( sem . try_acquire_for ( std :: chrono :: milliseconds ( 100 ))) { std :: println ( "First wait succeeded" ); } else { std :: println ( "First wait timed out" ); } if ( sem . try_acquire_for ( std :: chrono :: milliseconds ( 300 ))) { std :: println ( "Second wait succeeded" ); } else { std :: println ( "Second wait timed out" ); } } See @Compiler Explorer A typical result is: First wait timed out Second wait succeeded The first call stops waiting before the notifier calls release() . It does not decrease the counter. Later, the notifier increases the counter, and the second call succeeds. A timeout is not an exact scheduling deadline. Waiting for 100 milliseconds means the function will not report a timeout before that duration has passed. The operating system may resume the thread a little later. Semaphore, mutex, or condition variable? These tools solve different problems. Use a mutex when one thread should own a protected section. The thread that locks the mutex must unlock it, and std::lock_guard makes that ownership easy to manage. Use a counting semaphore when up to N operations may run at the same time. Use a binary semaphore when one thread needs to send a simple signal to another thread. A condition variable is a better fit when threads wait for shared state to satisfy a condition. It normally works together with a mutex and a predicate. Tool Common use std::mutex One thread owns a protected section std::counting_semaphore Up to N operations run together std::binary_semaphore One thread signals another std::condition_variable Threads wait for a shared-state condition A semaphore also remembers an unused counter value. If release() runs before another thread starts waiting, a later acquire() can still use that value. With a condition variable, code normally checks shared state rather than relying on a stored notification. Details worth knowing LeastMaxValue is a lower bound In: std :: counting_semaphore 3 > the value 3 means that the implementation must support a counter of at least three. Its real maximum may be larger. max() returns the value supported by the implementation. Application code should normally use the number it actually needs and not depend on any extra range offered by one standard library. Do not increase the counter past max() release(update) requires that the new counter value does not exceed max() . This matters with std::binary_semaphore . It is not an event flag that can be set many times without being cleared. Calling release() twice without an acquire() between those calls may break the functionβs precondition. try_acquire() may fail spuriously try_acquire() performs one non-blocking attempt. It is allowed to return false even when the counter is greater than zero. Use acquire() when the thread must wait. Use try_acquire_for() or try_acquire_until() when it should wait only for a limited time. Waiting order is not guaranteed If several threads are waiting, the standard does not say that the oldest waiter must run first. Code that needs strict first-in, first-out behavior requires an extra queue or scheduling layer. Watch the lifetime Do not destroy a semaphore while another thread may still be using it. All calls to acquire() , release() , and the timed functions must finish before the semaphoreβs lifetime ends. Summary std::counting_semaphore is useful when several threads may run an operation together, but their number must stay below a fixed limit. std::binary_semaphore works well for simple signals between threads. Use a mutex for normal exclusive access and a condition variable when threads wait for a shared-state condition. The main thing is to be clear about what the counter means. It may represent free worker slots, connections, buffers, or a signal waiting to be received. References std::counting_semaphore and std::binary_semaphore - cppreference C++ working draft: semaphores Implementing C++20 semaphores - Red Hat Developer C++ Concurrency in Action, Second Edition by Anthony Williams Concurrency with Modern C++ by Rainer Grimm Back to you Do you use semaphores mainly for limiting concurrent work or for signaling? Have you tried C++20 or some third-party libraries?πC++ StoriesFriday, August 7, 2026
A Collection of Structured PromptsHow-tos, rulebooks, lessons, and prompts.πMy Very Best AI Slop
Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 5Cheating with std::unique_ptr . The post Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 5 appeared first on The Old New Thing .πThe Old New Thing
Link What You Include - Maintain a Coherent CMake Target Model - Frank Miller - C++Now 2026π₯CppNow
A Brief History of Data Storage - Eli Holderness - NDC Copenhagen 2026π₯NDC Conferences
Commercialising Audio Plugins - Going From Development to Sales and Beyond - Tobias LΓΈnnerΓΈd Madsenπ₯audiodevcon
Everything a .NET developer needs to know about configuration & secret management -Sander ten Brinkeπ₯NDC Conferences
NDC Porto - 17-20 Nov 2026 π΅πΉ #softwaredeveloper #conference #portugalπ₯NDC Conferences
Neural Rendering for Subsurface Scattering - Cyrill Mikschπ₯MeetingCpp
Designing REST APIs for the age of AI agents - Boyan Mihaylov - NDC Copenhagen 2026π₯NDC Conferences
header guards [C++ Shorts Lesson 32]π₯Mike ShahThursday, August 6, 2026
Kitware Awarded up to $17.5 Million ARPA-H Contract to Develop Virtual Testing Platform for Robot-Assisted Stroke Interventions3.5-year TAIR project will create a realistic, patient-specific virtual environment to accelerate the development and evaluation of robotic stroke devices. Clifton Park, NY β August 6, 2026 β Kitware has been awarded up to $17.5 million by the Advanced Research Projects Agency for Health (ARPA-H), an agency within the U.S. Department of Health and Human [β¦]πKitware Inc
Lightning Talk: RPC With Coroutines, RAII & Callable Weakpointers - Edward Boggis-Rolfeπ₯CppOnline
Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4Moving things around if possible. The post Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4 appeared first on The Old New Thing .πThe Old New Thing
Why is shared ptr 16 Bytes?! π€― | C++π₯CppNuts
Imagine If We Made It Simple - Gui Ferreira - NDC Copenhagen 2026π₯NDC Conferences
Measurement uncertainty and measured constantsMeasurement uncertainty and measured constants When you write constexpr double G = 6.674e-11; in your code, the type system makes a claim that is not true. It claims the value is exact. It is not. The Newtonian constant of gravitation is one of the least precisely known constants in physics. CODATA 2018 lists it as 6.674 30(15) Γ 10β»ΒΉΒΉ mΒ³ kgβ»ΒΉ sβ»Β² , and the (15) means the fifth significant digit is already uncertain. Only four digits of G are actually known. Every result derived from that double inherits an uncertainty that the program neither tracks nor reports. The code happily prints ten significant digits of a solar mass computed from a constant that guarantees four. mp-units now models this honestly. This post introduces the uncertain representation type, the standard_uncertainty and relative_standard_uncertainty metadata for measured constants, and the measurement_of helper that connects them.πmp-unitsWednesday, August 5, 2026
Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 3Forcing an object into the global interface table. The post Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 3 appeared first on The Old New Thing .πThe Old New Thing
Evolving AI chat with MCP Apps - Phil Nash - NDC Copenhagen 2026π₯NDC Conferences
AI Security in Practice: Protecting Your AI-Powered Applications - Olivia Liddellπ₯NDC Conferences
Tour of Agent Protocols: MCP, A2A, AG-UI, A2UI - Mete Atamel - NDC Copenhagen 2026π₯NDC Conferences
How I helped developers talk about feelings and needs - Gitte Klitgaard - NDC Copenhagen 2026π₯NDC Conferences
Capturing and Transferring Expressive Microtiming in Drumming - Eemi Fagerlund - ADCx Copenhagenπ₯audiodevcon
Day1 room4 video6π₯NDC Conferences
Inside Todayβs AI-Accelerated Software Supply Chain Attacks - Mackenzie Jacksonπ₯NDC Conferences
How to make an internal conference worth our time - Anna Kvashchuk - NDC Copenhagen 2026π₯NDC Conferences
SVGs, ah yes the crazy files with XML that make pretty figures - Kristoffer Strubeπ₯NDC Conferences
Vibes Not Vulns: Securing the Era of AI-Written Software - Mackenzie Jacksonπ₯NDC Conferences
.NET supply chain: Protecting against hidden threats - Tom van den Berg - NDC Copenhagen 2026π₯NDC Conferences
Write Drunk, Edit Sober: Creating Generative Content Responsibly - Matthijs van der Veerπ₯NDC Conferences
Application Performance Optimisation in Practice - Steve Gordon - NDC Copenhagen 2026π₯NDC Conferences
Slang shader reflection and engine integration in C++π₯MeetingCpp
AI Agents In-Depth β Function Calling, MCP and Tool Use Under the Hood - Alan Smithπ₯NDC Conferences
ASP.NET Core Authentication - The Dirty Details - Chris Klug - NDC Copenhagen 2026π₯NDC Conferences
AI-Powered Gamification for the Web - Courtney Yatteau - NDC Copenhagen 2026π₯NDC Conferences
header files - interface (.hpp) and implementation (.cpp)[C++ Shorts Lesson 31]π₯Mike Shah
ColourfieldsOver on the (inexplicably login-walled) site "NewEnigma," Keith Austin [writes](https://www.newenigma.com/enigma/view/1277/Colourfields): > Draw a 4-by-4 grid. Colour each square red or blue. Select any square, S, and write 1 in it. > Then write 1 in every square you can reach from S by a series of moves, where each move is from > a square to an adjacent, horizontally, vertically, or diagonally, square of the same colour [...] In brief: What is the maximum number of _queenwise-connected_ monochromatic regions you can make by coloring each square of an $$n\times n$$ grid either red or blue?πArthur OβDwyer
New bazel.build websites incoming!We're happy to announce the launch of the new bazel.build documentation site and the new web UI for the Bazel Central Registry! New documentation site Last year, Alan Mond wrote a viral blog post that highlighted the issues with Bazel's documentation site. This led to his Let's Build the Future of Bazel Documentation birds-of-a-feather session at BazelCon 2025, where we decided to overhaul the documentation completely. Alan and a group of volunteers from the community and Google have now completed the first and most significant milestone by migrating https://bazel.build from a proprietary Google hosting site to Mintlify . This step improves the experience for contributors and readers alike: It's easier to make (significant) changes to the docs since we now use the well-documented .mdx format. Moreover, contributors can now generate a preview of their changes either locally or by using the new automated PR preview feature ( example ). For readers, Mintlify offers AI features such as an embedded assistant that allows them to prompt the docs directly. Documentation content continues to live in the bazelbuild/bazel GitHub repository next to the Bazel source code. The new bazel-contrib/bazel-docs repository contains the documentation pipeline and the navigation. There are still some rough edges that we plan to address in the near future. Known issues are being tracked here - please post a comment when you encounter a bug. We hope that the new website makes it easier for external developers to contribute to the documentation, especially when it comes to significant changes like revamping the structure or adding new pages such as codelabs and tutorials. Consequently, we welcome anyone to contribute to these efforts - please check https://bazel.build/contribute/docs for instructions, or visit us in the #documentation Slack channel . New BCR UI Community member Paul Johnston has built an entirely new web UI for the Bazel Central Registry at https://registry.bazel.build/ . The new site brings several improvements over the previous one: Detailed overview : The new site features a denser display of key information about the module, including its dependencies, maintainers, and other versions. It also puts the prose documentation of the module front and center, pulled directly from the README.md file of the corresponding GitHub repo. Smarter information retrieval : Beside pulling README files, the new site has quite a few new tricks up its sleeve. It tries to surface official API documentation from the module, and if it's missing, it runs Stardoc on the .bzl files automatically and surfaces the result. It also presents the attestations and presubmit configs associated with each module version. Powerful search and indexes : Beyond searching for modules by their names, you can also search by symbols - try searching for go_library . You can also view the list of available modules filtered by their primary language or maintainers. Faster UI update times : Module authors should expect quicker updates to the BCR UI. When new commits land in the BCR GitHub repo, a repository dispatch triggers a new BCR UI build that takes on average less than 5 minutes. The new static assets are deployed to GitHub Pages in another 1-2 minutes. Other tidbits : The new site has many other corners to explore. One of them is the Bazel flags page , which lists all the command-line flags present in the past few Bazel LTS releases. For each individual flag, it also tells you which Bazel versions it is present in, and which Bazel subcommands it applies to. Please take it for a drive, and file issues and send PRs to https://github.com/bazel-contrib/bcr-frontend ! Finally, a huge thank you again to all community members who contributed to these efforts, especially Kayce Basques, Alex Eagle, Paul Johnston, Alan Mond, Armando Montanez, Anthony Pratti, Nikki Vijaybhaskar and Kapunahele Wong - they wouldn't have been possible without you!πBazel BlogTuesday, August 4, 2026
How PVS-Studio helps enhance quality of embedded projectsEmbedded development comes with a wide variety of custom configurations, compilers, and build systems, which can make standard analysis approaches difficult to apply. To address this challenge...πfrom pvs-studio.com
BANFF-AID: AI-powered Banff ScoringBefore donated organs can be used, pathologists assess histologic features (including several features covered by Banff lesion scores) based on their overall health and whether key chronic injury indicators are discovered. Unfortunately, this evaluation is largely subjective, resulting in conservative decision-making and the discarding of viable donor organs. The scoring process is also very time-consuming [β¦]πKitware Inc
cppdashboard.dev β The aggregator of what matters in C++ right nowI am excited to announced I launched cppdashboard.dev, a website that aggregates the activity of the C++ community into one place. The post cppdashboard.dev β The aggregator of what matters in C++ right now first appeared on Marius Bancila's Blog .πMarius Bancila's Blog
Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 2Using a reference stored in the global interface table. The post Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 2 appeared first on The Old New Thing .πThe Old New Thing
How To Make Formal Methods A Software Quality Solution That Can Actually Be Used In The Industryπ₯CppNow
Glenn Joseph Fernandez: Why "Contributor" Is the Highest Title in Boostπ₯C++ Alliance
Between the Layersβ Interpreting Large Language Models - Michelle Frost - NDC Toronto 2026π₯NDC Conferences
Principle Misunderstandings - Kevlin Henney - NDC Toronto 2026π₯NDC Conferences
How do compilers see your code? If you've ever wondered about that, you're in the right place.π₯PVS-Studio
MΓΆbius-strip crosswordsRandall Rothenberg in the _New York Times_ (1988-08-10) writes that although ["Puzzle Makers Exchange Cross Words"](https://archive.is/K2Hw8) about the fall of ERNE and ESNE and the rise of XEROX and PEACE PLAN, one Robert Guilbert Sr. > was ignorant of the dispute two years ago when he developed a competitive crossword game > called Pago Pago and first became aware of crossword fans' passion for the pastime.πArthur OβDwyer
More compile-time checks with std::nullptr_tIn today's post, I like to show you the benefit of a often unknown type: std::nullptr_t . You have that data type in the language since C++11, together with the much more frequently used nullptr . While nullptr is the value, std::nullptr_t is the data type that can hold β¦πAndreasFertig.com
An Honest Review of AI ProgrammingThey gave me a Claude subscription and told me to get tokenmaxxing, so I tried to give it a fair shot.πMathieu RopertMonday, August 3, 2026
CMake 4.4.2 available for downloadCMake 4.4.2 available for downloadπKitware Inc
Where Did Half My C++ Object Go?! π± | Object Slicing Explainedπ₯CppNuts
Your Docs Have a New Reader (and It Hallucinates) - Paul Wickingπ₯CppOnline
Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 1Looking for a COM reference that is between strong and weak. The post Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 1 appeared first on The Old New Thing .πThe Old New Thing
C++ Weekly - Ep 544 - UB & Ranges of Enumerations & constexprπ₯Jason Turner
How I Learned to Love the Docs: Documentation As Design Process for Music Tech Products - Astrid Binπ₯audiodevcon
Modular Slang Shader Factory in C++ - Oleksandr Volinskyiπ₯MeetingCpp
raii [C++ Shorts Lesson 30]π₯Mike Shah
C++ and AI Sitting in a Treeβ¦π₯GlobalCpp