Computing day of year in C++

I have been recently asked on my post on the date library if the library has a function for computing the day of the year. It actually does not, although it is fairly simple to compute it.

UPDATE: Howard Hinnant has shown in a comment below how to write a day_of_year() function using the date libray.

Let’s look at the days of the year.

Day Day of year
January 1 1
January 2 2
January 31 31
February 1 32
February 28 59

Here is where things complicate a bit, because during leap years February has 29 days. So we actually need to have two counts of days.

Day Day of non-leap year Day of leap year
January 1 1 1
January 2 2 2
January 31 31 31
February 1 32 32
February 28 59 59
February 29 N/A 60
March 1 60 61
December 31 365 366

It is fairly simple to compute the day of the year based on the day of the month if we knew the day of the year of each first day of the month. That can be also put in a table.

Day of month Day of non-leap year Day of leap year
January 1 1 1
February 1 32 32
March 1 60 61
April 1 91 92
May 1 121 122
June 1 152 153
July 1 182 183
August 1 213 214
September 1 244 245
October 1 274 275
November 1 305 306
December 1 335 336

So we can compute the day of the year as:

We can simplify that a bit by subtracking 1 from the day of the year values in the table above, such that January 1st is the day 0, February 1st is day 31, etc.

The following code sample shows how this can be written in C++:

And how it can be used:

This day_of_year() function can be used with the date library too. I’ll just add one more utility function that takes a date::year_month_day value and returns the day of the year.

And we want to know what day of the year today is then we can do that too:

The day_of_year() function is very simple and does not do any argument checks. That makes it possible to compute dates such as 2017.08.55 or 2017.55.100. Obviously, these not only that do not make sense, but indexing the days_to_month array beyond its bounds is undefined behavior. That means that in practice you should write a function that validates the arguments and throws an exception upon error. However, in this case, the day_of_year() can not be constexpr anymore.

This would throw an exception on dates like 2017.13.1 or 2017.1.50, but would not do so for 2017.2.30 or 2017.11.31 that are also invalid dates. That can be further corrected by verifying that the day of the month does not exceed the number of days that month can have in the given year.

, , , Hits for this post: 789 .

You may have multiple versions of the .NET framework installed and used on your machine. The framework has two components: the set of assemblies that provide functionalities for your application, and the common language runtime (CLR) that handles the execution of the application. These two components are versioned separately. If you what to see what versions of the framework are installed, you have to query the Windows Registry. If you want to know what versions of the CLR are installed you could either use clrver.exe or do it programmatically. In this article, we will look at this later option and how to do it in C++.

How to: Determine Which .NET Framework Versions Are Installed

To query the installed CLR versions from C++ we have to:

In order to call CLRCreateInstance we must include the metahost.h header and link with the Mscoree.lib static library.

To use the ICLRMetaHost and ICLRRuntimeInfo interfaces we must import the mscorlib.tlb type library. The _COM_SMARTPTR_TYPEDEF are used for defining COM smart pointers ICLRMetaHostPtr and ICLRRuntimeInfoPtr that automatically handle the reference counter of the underlying COM object.

The call to the EnumerateInstalledRuntimes method, when successful, returns a pointer to an IEnumUnknown interface. This enables enumerating through a component that contains multiple objects. Its method Next retrieves a specified number of items. In this implementation that number is 1. The return value is a pointer to the IUnknown interface, but what we are enumerating through are actually ICLRRuntimeInfo interfaces.

To retrieve the version info we must use the GetVersionString method of ICLRRuntimeInfo. The arguments are an array of wide characters that will receive the string and the size of the array. In order to retrieve the necessary size of the buffer, we have to first call the method with null for the first argument. In this case the function returns ERROR_INSUFFICIENT_BUFFER as a HRESULT (i.e. HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) and sets the second argument to the necessary size. After allocating the necessary buffer, we call the method again, providing the buffer and its size.

Running this program on my machine prints the following (which is the same as the output from clrver.exe).

, , , Hits for this post: 907 .

Firebase is a platform for developing mobile and web application that provides analytics, authentication, real-time databases, notifications, cloud messaging, crash reporting and other services for your application. In this post, I will discuss how to use the Firebase Analytics in Cordova applications for measuring app usage and user engagement.

Analytics for Firebase

This is a free service that can be used for both Android and iOS applications. It provides unlimited reporting of up to 500 distinct events and a number of user properties.

Events allow you to see what happens in an application, such as user actions, system events, or errors. There is a number of automatically collected events and you can define your own events. However, you have to keep in mind that:

  • there is a limit of 500 distinct events that can be reported.
  • each event can have up to 25 parameters associated with it.
  • event names can be up to 40 characters long, may only contain alphanumeric characters and underscores (“_”), and must start with an alphabetic character.
  • “firebase_”, “google_” and “ga_” prefixes are reserved and cannot be used.
  • names are case-sensitive and logging two events whose names differ only in case will result in two distinct events.

See FirebaseAnalytics.Event for more information.

User properties are information about the user that is recorded together with the events. Default collected user properties include user info (age, gender, country, language, interests), device info (brand, category, model) and app info (version, app store, first open time, new/established).

Appart from the implicitly collected user properties you can define your own. For that, however, you must use the Firebase console. You must be careful with the properties that you add because for now these cannot be renamed or deleted (you can only change their description).

Getting started

To be able to use Firebase for your Android or iOS app you need to enroll it. For that, you must create a project and then add applications to it.

  1. Open the Firebase console.
  2. Create a new project (or select an existing one).
  3. Add a new Android, iOS or web app.
  4. Follow to instructions to register the app, download the config files or perform other necessary setups.

If you need more help for setting up your app read the Firebase documentation or additional web resources.

Cordova plugin

There is an official Cordova plugin called cordova-plugin-firebase that provides push notifications, analytics, event tracking, crash reporting and other services from the Firebase platform for you Cordova application.

When you start using Firebase, make sure you have the config files properly setup (present and valid). Otherwise, your app will crash on boot or Firebase features won’t work.

Recording events and user properties

To record a user property you should call setUserProperty. This has the form:

The name is the case-sensitive name you defined in the Firebase console. In the example above the name was login_type and possible values were demo and live. The purpose of this is to indicate that for an application that supports a demo mode where the users don’t have to create an account to try the app, the user is either properly authenticated on or actually using the demo feature.

When this call is made the Debug view shows the change of the user property value in the live stream.

If you look at the recorded user properties for all following events you will see the logon_type with the demo value attached to it.

As mentioned ealier, there are a series of events that are automatically recorded. These include screen_view (collected whenever the page has changed) and user_engagement (collected periodically when the app is in foreground). However, you can send any other event, each with any number of parameters. To record events you must use the logEvent function that has the following form:

Let’s suppose that in your app users can bookmark items (whatever those items might be) but also remove them from the bookmarks (or favorites). You want to record the event of adding and removing an item. You can define an event called favorites_action with two parameters: action that can be either add or remove, and type, that indicates what kind of item was added or removed.

These are added automatically to the stream of events, recorded with both the the auto collected and user defined parameters and user properties.

All the events have several implicit parameters, including firebase_screen, firebase_screen_class, and firebase_screen_id. Screen class is the class name of the Activity, and the ID is a numerical ID that is probably hard to use to figure where the event came from. The screen name on the other hand (firebase_screen) is what you probably want to look at. This can be set with a call to setScreenName. All events will have the param firebase_screen set to the last value passed to setScreenName. Therefore, you probably want to make this call whenever the user opens a new page in your application. The call has the form:

Here is an example:

Debugging Firebase Analytics

The events recorded by Firebase are not instantly sent to the servers. Instead they are cached, archived and periodically dispatched. However, you can enable live streaming of events for debugging purposes, both on Android and iOS. This was used for all the screen shots used previously.

For Android devices:

  • To enable execute:
  • To disable execute:

For iOS devices:

  • To enable specify -FIRDebugEnabled as an argument in XCode
  • To disable specify -FIRDebugDisabled as an argument in XCode

For more information see Debugging Events.

, , , , , , Hits for this post: 989 .

I am pleased to announce that my book on modern C++ programming has been published by PacktPub. The book is called Modern C++ Programming Cookbook and can be ordered at and Amazon. ISBN of the book is 9781786465184. The complete table of contents is available below.

The front cover

The book is organized in recipes, much like a cookbook (therefore the name). These recipes are organized in sections that introduce you to the topic, list any necessary pre-requisites and then explain how to do something and how that works. Throughout 112 recipes, the book covers both language and library features from C++11, C++14 and C++17, including the libraries for strings, containers, algorithms, iterators, input/output, regular expressions, threads, filesystem, atomic operations, and utilities. Besides that, there is a chapter for patterns and idioms and one dedicated for testing frameworks, that covers everything you need to know to get started with Boost.Test, Google Test and Catch.

This book is intended for all C++ developers, regardless of their experience. The beginner and intermediate developers will benefit the most from the book in their attempt to become prolific with C++. Experienced C++ developers, on the other hand, will find a good reference for many C++11, C++14, and C++17 language and library features that may come in handy from time to time. However, the book requires prior basic knowledge of C++, such as functions, classes, templates, namespaces, macros, and others. If you are not familiar with C++ at all, you should first read an introductory book to familiarize yourself with the core aspects.

Although C++17 has not yet been ratified as an ISO standard, the final version that is up for the ballot is well defined. In my book I discuss most of the important language and library features that made it into C++17. The C++17 features discussed in the book are:

  • structured bindings
  • fold expressions
  • constexpr if
  • new attributes ([[fallthrough]], [[nodiscard]], [[maybe_unused]])
  • new type deduction rules for list initialization
  • range based for loops improvements
  • general form of lambda expressions
  • std::invoke() and std::apply()
  • static_assert changes
  • non-member container access functions std::data(), std::size(), and std::empty()
  • std::search() searchers (Boyer-Moore and Boyer-Moore-Horspool)
  • chrono changes (floor(), round(), ceil(), and abs())
  • std::any
  • std::optional
  • std::variant (2 recipes)
  • std::string_view
  • std::scoped_lock
  • filesystem library (5 recipes)
  • shared_ptr and unique_ptr changes

All the samples in the book have been tested with VC++ 2017 (where possible), GCC 7 and Clang 5. All the language and library features discussed in the book are available with these versions of the mentioned compilers, except for a few exceptions for VC++. At this time, the following features are still not supported in VC++:

  • structured bindings
  • fold expressions
  • constexpr if
  • searchers for std::search()

If you do not have the latest versions of these compilers, you can try all the samples in the book with an online compiler. gcc and Clang are available at and VC++ is available at

Table of contents

  1. Learning Modern Core Language Features
    • Using auto whenever possible
    • Creating type aliases and alias templates
    • Understanding uniform initialization
    • Understanding the various forms of non-static member initialization
    • Controlling and querying object alignment
    • Using scoped enumerations
    • Using override and final for virtual methods
    • Using range-based for loops to iterate on a range
    • Enabling range-based for loops for custom types
    • Using explicit constructors and conversion operators to avoid implicit conversion
    • Using unnamed namespaces instead of static globals
    • Using inline namespaces for symbol versioning
    • Using structured bindings to handle multi-return values
  2. Working with Numbers and Strings
    • Converting between numeric and string types
    • Limits and other properties of numeric types
    • Generating pseudo-random numbers
    • Initializing all bits of internal state of a pseudo-random number generator
    • Using raw string literals to avoid escaping characters
    • Creating cooked user-defined literals
    • Creating raw user-defined literals
    • Creating a library of string helpers
    • Verifying the format of a string using regular expressions
    • Parsing the content of a string using regular expressions
    • Replacing the content of a string using regular expressions
    • Using string_view instead of constant string references
  3. Exploring Functions
    • Defaulted and deleted functions
    • Using lambdas with standard algorithms
    • Using generic lambdas
    • Writing a recursive lambda
    • Writing a function template with a variable number of arguments
    • Using fold expressions to simplify variadic function templates
    • Implementing higher-order functions map and fold
    • Composing functions into a higher-order function
    • Uniformly invoking anything callable
  4. Preprocessor and Compilation
    • Conditionally compiling your source code
    • Using the indirection pattern for preprocessor stringification and concatenation
    • Performing compile-time assertion checks with static_assert
    • Conditionally compiling classes and functions with enable_if
    • Selecting branches at compile time with constexpr if
    • Providing metadata to the compiler with attributes
  5. Standard Library Containers, Algorithms, and Iterators
    • Using vector as a default container
    • Using bitset for fixed-size sequences of bits
    • Using vector for variable-size sequences of bits
    • Finding elements in a range
    • Sorting a range
    • Initializing a range
    • Using set operations on a range
    • Using iterators to insert new elements in a container
    • Writing your own random access iterator
    • Container access with non-member functions
  6. General Purpose Utilities
    • Expressing time intervals with chrono::duration
    • Measuring function execution time with a standard clock
    • Generating hash values for custom types
    • Using std::any to store any value
    • Using std::optional to store optional values
    • Using std::variant as a type-safe union
    • Visiting a std::variant
    • Registering a function to be called when a program exits normally
    • Using type traits to query properties of types
    • Writing your own type traits
    • Using std::conditional to choose between types
  7. Working with Files and Streams
    • Reading and writing raw data from/to binary files
    • Reading and writing objects from/to binary files
    • Using localized settings for streams
    • Using I/O manipulators to control the output of a stream
    • Using monetary I/O manipulators
    • Using time I/O manipulators
    • Working with filesystem paths
    • Creating, copying, and deleting files and directories
    • Removing content from a file
    • Checking the properties of an existing file or directory
    • Enumerating the content of a directory
    • Finding a file
  8. Leveraging Threading and Concurrency
    • Working with threads
    • Handling exceptions from thread functions
    • Synchronizing access to shared data with mutexes and locks
    • Avoiding using recursive mutexes
    • Sending notifications between threads
    • Using promises and futures to return values from threads
    • Executing functions asynchronously
    • Using atomic types
    • Implementing parallel map and fold with threads
    • Implementing parallel map and fold with tasks
  9. Robustness and Performance
    • Using exceptions for error handling
    • Using noexcept for functions that do not throw
    • Ensuring constant correctness for a program
    • Creating compile-time constant expressions
    • Performing correct type casts
    • Using unique_ptr to uniquely own a memory resource
    • Using shared_ptr to share a memory resource
    • Implementing move semantics
  10. Implementing Patterns and Idioms
    • Avoiding repetitive if…else statements in factory patterns
    • Implementing the pimpl idiom
    • Implementing the named parameter idiom
    • Separating interfaces from implementations with the non-virtual interface idiom
    • Handling friendship with the attorney-client idiom
    • Static polymorphism with the curiously recurring template pattern
    • Implementing a thread-safe singleton
  11. Exploring Testing Frameworks
    • Getting started with Boost.Test
    • Writing and invoking tests with Boost.Test
    • Asserting with Boost.Test
    • Using test fixtures with Boost.Test
    • Controlling output with Boost.Test
    • Getting started with Google Test
    • Writing and invoking tests with Google Test
    • Asserting with Google Test
    • Using test fixtures with Google Test
    • Controlling output with Google Test
    • Getting started with Catch
    • Writing and invoking tests with Catch
    • Asserting with Catch
    • Controlling output with Catch


It took about eight months to complete this book and I got a lot of help from several people that I would like to thank to. First of all, is the team at PacktPub; although there were more people involve that I actually am aware of, I would like to thank Anurag Ghogre, Subhalaxmi Nadar and Nitin Dasan for all the help they provided throughout this time and the work they put in the project, as well as the other people that were involved with this book. I also want to thank David Corbin, whom I know for many years as “The CPU Wizard”, for reviewing the book and providing valuable feedback that made the book better. And last, but not least, I want to thank my wife for putting up with me through the many days and nights that I worked on this project.

, , , , , , , , Hits for this post: 8809 .

Initialization of variables in C++ can have several forms:

C++11 introduced a generalized syntax for initialization with a braced initializer list, referred to as braced-init-list. Initialization with braced-init-list is called list initialization. There are two types of list initialization, each having multiple forms (check the above links), but simplified, we can have:

  • direct list initialization: T object {arg1, arg2, ...};
  • copy list initialization: T object = {arg1, arg2, ...};

Prior to C++17 the type for all the following objects (a, b, c and d) is deduced to std::initializer_list<int>. There is no difference between the direct-list-initialization and the copy-list-initialization on the result of the type deduction.

This, however, changed in C++17 that introduced the following rules:

  • for copy list initialization auto deduction will deduce a std::initializer_list<T> if all elements in the list have the same type, or be ill-formed.
  • for direct list initialization auto deduction will deduce a T if the list has a single element, or be ill-formed if there is more than one element.

As a result, the example above changes so that a and c are still std::initializer_list<int> but b is deduced as an int and d is ill-formed, because there is more than one value in the brace-init-list.

For more information about these changes see N3922: New Rules for auto deduction from braced-init-list.

, , , Hits for this post: 5490 .

In C#, function parameters can be declared with the ref or out modifier, the former indicating that a value is already set and the function can read and write it, and the later that the value is not set and the function must do so before returning.

As a side note, there is a good thread on this topic on StackOverflow, What’s the difference between the ‘ref’ and ‘out’ keywords?, with one answer nailing the question in a hilarious way.

C# 7 has extended the way these modifiers can be used:

  • out variables can be declared inline and used in the outer scope
  • ref can be used for locals and return values from functions

out variables

Prior to C#7, arguments passed to out parameters had to be declared before they are used. The following is a typical example:

The new change allows the declaration of the out variable inline where it is actually used.

You can either specify an explicit type, or use the var keyword and let the compiler infer the type.
The scope of the variable declared in this manner is leaked into the output scope of the statement where it appears.

ref locals and returns

The ref modifier has been extended to be used with local variables and return values.

Local variables can be declared with the ref modifier, in which case the variable is a reference to another storage. Local references must be initialized upon declaration, and cannot be assigned values (i.e. ref var i = 42;).

On the other hand, functions can return references to variables. In this case the ref modifier needs to be used both on the return type declaration and on every return statement.

In the following example Container is an implementation of a container that internally stores an array of elements of type T. Method at() takes an argument that represents the index of an element and returns a reference to that element, or throws an exception if the index is out of bounds. The return type is declared as ref T and the return statement is return ref.

Having an instance of this Container class we can gain access to its elements in order to read or write them using the at() method. Elements can be assigned new values directly ( = 42;) or could be bound to local variables.

There is an important difference between the last two examples:

  • var r1 =; the value of the second element in the container is copied to the local variable r1, even though the function returned a reference. Modifying this local variable will not affect the element in the container.
  • ref var r2 = ref; defines r2 as a reference to the third element in the container, and therefore modifying the local variable will actually modify the element in the container.

Returning a references to a local variable is not allowed (as its scope ends when the function returns, which would make the reference point to a storage that no longer exists).

You can read more about these changes in this article, What’s new in C# 7.

, , , Hits for this post: 4088 .

Matt Godbolt has announced today that the Visual C++ compiler is finally available on Compiler Explorer ( Compiler Explorer is a website where you can write C/C++/Rust/Go/D code, compile it with various compilers and settings and see the resulted assembly code.

The version available is 1910, i.e. VC++ 2017 RTM (the exact version number is 19.10.25017.0). The following targets are available:

  • x86: x86 CL 19 2017 RTW
  • x64: x86-64 CL 19 2017 RTW
  • ARM: ARM CL 19 2017 RTW

To give it a try, I compiled the following program:

The result may look at little bit surprizing, as it totals over 5000 lines of assembly code, as oposed to gcc 7 or clang 4 that only produce 42.

, , , Hits for this post: 5573 .

Visual Studio 2017 Enterprise provides a feature called Live Unit Testing that enables developers to see live how changing C# and VB.NET code affects its corresponding unit tests. Among its features, it includes showing coverage information in the editor as you type, integration with the Test Explorer, including/excluding targeted test methods or projects for large solutions, quick navigation to failed tests. It works with MSTest, and NUnit, but only for .NET Framework, as support for .NET Standard has not been added yet.

Detailed information about this productivity tool is available in this blog post: Live Unit Testing in Visual Studio 2017 Enterprise. In this article, I will show how this feature help you when you do test driven development. For this purpose, I will create a class called Point3D that is supposed to model a point in 3-dimentional space, as part of a .NET Framework class library called ALibraryHasNoName. A separate unit testing project called ALibraryHasNoName.Test contains an unit testing class Point3DTest. Initially, these are empty.

To start the live unit testing you need to run go to Test > Live Unit Testing and press Start.

I will start by writing a test for the constructor of Point3D. The constructor is supposed to take three arguments, representing the values for the coordinates in the three dimensions, X, Y and Z.

Since the Point3D class is empty, this line of code produces a compiling error. I can use the refactoring tools and create an implementation for the constructor.

After the code is added, when I navigate to the source code document I can see green check marks on the left of the code indicating lines of code covered by successful unit tests.

I don’t like the default names v1, v2, v3 and the fact that these values are represented by fields. I want to use properties called X, Y and Z. Therefore I am refactorying the generated code as shown below:

Back to the unit test, I am adding a few tests for these properties, making sure I get back the values I passed to the constructor.

The result when switching back to the Point3D class is that these properties are now covered by 1 test.

Next thing I want to do is add a static property/field to represent the origin of the 3D space, i.e. the point at (0,0,0). For this I am writing a new test method with the content shown below. Visual Studio suggests several ways to generate the missing symbol: as a property, field or read-only field, which I find the closest to what I want so I am going with this.

The code is generated and I get a visual indication that it is already covered by 1 test.

I want to add tests to make sure the three properties are all 0 as expected.

Back to the source code I can see now that each property is covered by two succesful tests at this point.

If you click the passing indicator, it shows the tests that cover it, whether they passed or failed, and the duration of their execution.

The last thing I want to do for this demo is adding a method to do a translation of the point in the 3d space. I want to call this method Offset. Therefore, I start with a new test method, called TestOffset where I create a point and invoke the new method. Visual Studio suggest to generate the missing method and I will go with the suggested code.

At this point the test is failing. Instead of a green check mark on the left, I see red crosses, indicating a failure.

These marks can be used to get more information about the failure. It turns out that the method is throwing an exception, that is not caught, so the test is failing.

Next, I will change the implementation of the Offset method to reflect what it is expected from it, and the red crosses turn instantly into green check marks.

To check the effect of the function, I am adding tests for each of the three properties of the class.

Going back to the class source code I can see that each of the three properties now has three successful tests that cover them.

Although this post only covers some of the capabilities of live unit testing I hope it shows how helpful it can be in writing unit tests in general and with TDD in particular. For details about the features check the official documentation.

, , , Hits for this post: 4800 .

Visual Studio 2017 has been officially launched today. The release notes contain a summary of all the changes available in the new version. This post is focused on the changes for C++ development.

The Visual C++ team has released a series of blog posts to document some of the new features. Here is a list of them:

Of all the changes and new features in VC++ 2017 (that are described in details in the articles mentioned above) there are several that I want to mention:

  • The C++ compiler is C++14 complete, but still lacks several C++98 and C++11 features. It also contains some features added to C++17.
  • The standard library implementation contains C++17 features including: any, optional, variant, string_view, make_from_tuple(). The complete list of improvements is available here.
  • Visual C++ 2017 runtime is compatible to the Visual C++ 2015 runtime. That means you can link to libraries build with VC++ 2015.
  • The C++ compiler version is 19.1, a minor release of the Visual C++ 2015 compiler (version 19.0). That means _MSC_VER is 1910. On the other hand, MFC and ATL are still on version 14.0 as in Visual C++ 2015. That means _MFC_VER and _ATL_VER ar both 0x0E00.
  • It is possible to open code from any folder with the Open Folder feature and get IntelliSense, navigation, building, and debugging capabilities without creating a solution and project first.
  • You can build your projects with CMake that is now supported in Visual Studio.
  • There is a built-in support for using another C++ compiler, such as Clang or GCC (mainly intended for building projects that target Android, Linux or Mac).
  • The C++ Core Checkers for enforcing the C++ Core Guidelines are now distributed with Visual Studio.
  • Installation of Visual Studio has been redesigned. Components are delivered in “workloads”, but individual components can be added or removed. For C++ there are five workloads: Universal Windows Platform development, Desktop Development with C++, Game development with C++, Mobile development with C++, and Linux development with C++.
  • Installation folder is not c:\Program Files (x86)\Microsoft Visual Studio 15.0 as with previous version, but c:\Program Files (x86)\Microsoft Visual Studio\2017\.

Here are a couple of screenshots from installing Visual Studio:

, , , Hits for this post: 7301 .

In my previous post, Dining Philosophers in C++11, I have provided an implementation for the dining philosophers problem using modern C++ features, such as threads and mutexes. However, it was noted in the comments that the implementation did not prevent the philosophers starving to death when you remove the waiting times.

An algorithm that prevents the philosophers from starving was proposed by Mani Chandy and J. Misra and is known as the Chandy/Misra solution. This is a bit different than the original problem because it requires the philosophers to communicate with each other. The algorithm, as described on Wikipedia, is the following:

  1. For every pair of philosophers contending for a resource, create a fork and give it to the philosopher with the lower ID (n for agent Pn). Each fork can either be dirty or clean. Initially, all forks are dirty.
  2. When a philosopher wants to use a set of resources (i.e. eat), said philosopher must obtain the forks from their contending neighbors. For all such forks the philosopher does not have, they send a request message.
  3. When a philosopher with a fork receives a request message, they keep the fork if it is clean, but give it up when it is dirty. If the philosopher sends the fork over, they clean the fork before doing so.
  4. After a philosopher is done eating, all their forks become dirty. If another philosopher had previously requested one of the forks, the philosopher that has just finished eating cleans the fork and sends it.

In order to implement this, we must make several changes to the solution proposed in the previous post:

  • forks and philosophers must have identifiers
  • there is an initial setup of both forks and philosophers
  • use std::condition_variable to communicate between threads
  • increase the number of philosophers

Because it has been also argued that string_view is only available in C++17 and this implementation is supposed to work in C++11, I have replaced that with std::string const&.

In this implementation, philosophers, i.e. threads, need to communicate with each other to request the forks, i.e. resources. For this, we will use a std::condition_variable, which is a synchronization primitive that enables the blocking of one or more threads until another thread notifies it. A std::condition_variable requires a std::mutex to protect access to a shared variable. The following class, sync_channel, contains both a condition variable and a mutex and provides two methods: one that waits on the condition variable, blocking the calling thread(s), and one that notifies the condition variable, unblocking all the threads that are waiting for a signal.

The table class from the previous implementation is modified: the forks are no longer defined here, but a sync_channel is used to prevent philosophers start dining until the table setup is completed. Its name has been changed to table_setup.

The fork class is no longer a wrapper for a mutex. It has an identifier, an owner, a flag to indicate whether it is dirty or clean, a mutex, and a sync_channel that enables owners to request used forks. It has two methods:

  • request() that enables a philosopher to request the fork. If the fork is dirty, it is set to clean, and the ownership is given to the philosopher that asked for it. If the fork is clean (i.e. the current owner is eating), than the philosopher that asked for it will block, waiting for it to become dirty (i.e. the current owner has finished eating).

  • done_using() a philosopher indicates that has finished eating and notifies other philosopher that is waiting for the fork that it can have it.

There are less changes to the philosopher class: it has an identifier, and there are no more waiting times to simulate eating and thinking. There are some small changes to the following methods:

  • dine(): each philosopher only starts eating after the entire table has been setup. A condition variable, from the table_setup object is used for this.

  • eat(): each philosopher first requests the left and right fork. When they are available, they are locked using std::lock() to avoid possible deadlocks, and then their ownership is transfered to a std::lock_guard object, so they are properly released when done. After eating, the fork is set as dirty and other philosophers waiting for it are notified of this.

According to the initial setup, each fork is given to the philosopher with the lower ID. That means fokm 1, placed between philosopher 1 and N, goes to philosopher 1. Fork 2, placed between philosophers 2 and 3 is given to philosopher 2. Eventually, fork N, placed between philosophers N and 1, is given to philosopher 1. Overall, this means all philosophers have initially 1 fork, except for the first one that has two, and the last philosopher, that has none.

Put all together, the code looks like this:

The output of the program looks like this:

, , , , , , Hits for this post: 8453 .