Articles & Books

Let's Enumerate the UB -- Shafik Yaghmour

ShafikYaghmour.jpgSome time ago I started working on P1705 Enumerating Core Undefined Behavior, I have collected a large set of undefined behavior (UB) during that time. There is going to be a lot of work involved in getting the annex into shape and editing it into the standard. While this work is ongoing, I will take some time to write blog posts to explore the set of undefined behaviors.

Let's Enumerate the UB

By Shafik Yaghmour

From the article:

In these posts I will cover: why we have each undefined behavior, some consequences of running afoul of each UB and tools we can use to catch UB. My plan is to enumerate them in the order they appear in the standard.

IFNDR (What is that?)

Before we go on, let’s talk about a term we find in the standard, ill-formed no diagnostic required; also known as IFNDR. This is like undefined behavior in that the standard places no requirements on a program that contains IFNDR constructs, see [intro.compliance.general]p2.2 which says (emphasis mine):

If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program.

compared to the defintion of undefined behavior which says:

behavior for which this document imposes no requirements

The main difference is that IFNDR is a static property of your code (it is a propety of the program).

C++23: Deducing This -- Rainer Grimm

grimm-deducingthis.pngAnyone who thinks a small C++ standard follows a significant C++ standard is wrong. C++23 provides powerful extensions to C++20. These extensions include the core language, particularly the standard library. Today, I present a small but very impactful feature of the core language: deducing this.

C++23: Deducing This

By Rainer Grimm

From the article:

Deducing this, sometimes also called explicit object parameter, allows it to make the implicit this pointer of a member function explicit. Like Python, the explicit object parameter must be the first function parameter and is called in C++, by convention, Self and self.
struct Test {

    void implicitParameter();               // implicit this pointer
    void explictParameter(this Self& self); // explicit this pointer
};
Deducing this enables new programming techniques in C++23: deduplication of function overloading based on the object’s lvalue/rvalue value category and constness. Additionally, you can reference a lambda and invoke it recursively. Furthermore, deducing this simplifies the implementation of CRTP

How To Address 7 Major C++ Pain Points with CLion -- Anastasia Kazakiova

Anastasia-200x200.jpgIn the 2023 Annual C++ Developer Survey conducted by the C++ Foundation, the community identified a number of major pain points when working with C++.

How To Address 7 Major C++ Pain Points with CLion

By Anastasia Kazakiova

From the article:

As we’ll discuss in this article, CLion can help C++ developers with most of these in various ways:

  • Managing third-party libraries
  • Improving build times
  • Setting up a CI-pipeline
  • Managing a CMake project
  • Checking code for thread, memory and type safety issues on-the-fly
  • Setting up a development environment from scratch
  • Modernize your code

RFC - fat pointer class, evolution of YOMM2 -- Reddit Q&A

reddit.pngQuestion from reddit poster: I am preparing to release a new feature, a fat pointer class (virtual_ptr), that makes method dispatch even more efficient. Dispatching a method with one virtual argument via a virtual_ptr takes only three instructions and two independent memory reads. As an interesting side-effect, it is now possible to use YOMM2 with non polymorphic classes hierarchies.

RFC - fat pointer class, evolution of YOMM2

From the discussion:

I am preparing to release a new feature, a fat pointer class (virtual_ptr), that makes method dispatch even more efficient. Dispatching a method with one virtual argument via a virtual_ptr takes only three instructions and two independent memory reads. As an interesting side-effect, it is now possible to use YOMM2 with non polymorphic classes hierarchies.

Once I increase the semantic minor version number, I will mostly be stuck with the API. So, if you are interested and in the mood, please review the documentation.

Beyond this, I have been mulling a major version bump. There are a few stuff that are deprecated, and I would like to get rid of them.

Also, I wonder if it would be a good time to switch to C++20. The internals would be a lot simpler and cleaner if I could use concepts.

I have a few major developments on the radar:I have a few major developments on the radar:

  • dispatch on std::any and std::variant
  • "fat" versions of std::any and std::variant (in the manner of virtual_ptr)
  • going headers only
  • related to the former: tunable runtime
  • quasi static initialization of method tables

I wonder if it would be better to wait until these items are completed before switching to C++20 and putting the C++17 code in bugfix only mode.

Finite State Machine with std::variant - Vending Machine -- Bartlomiej Filipek

In my last article, we discussed Finite State Machines based on std::variant and some cool C++17 techniques. Today I want to go further and show you an example of a Vending Machine implementation.

Finite State Machine with std::variant - Vending Machine

By Bartlomiej Filipek

From the article:

Here’s a basic diagram that illustrates how the machine is going to work:

vending_states.png

Following the model from the last article, each state and the event will be a separate small structure. Later we can “pack” them into std::variant.

C++23: The Next C++ Standard -- Rainer Grimm

TimelineCpp.pngC++23 will be the next C++ standard after C++20. This new standard significantly improves C++ but is less game-changing than C++98, C++11, or C++20. C++23 is more in the tradition of C++17.

C++23: The Next C++ Standard

By Rainer Grimm

From the article:

The C++ Standards

C++ is more than 40 years old. What happened in the last years? Here is a simplified answer ending in C++23.

C++98

At the end of the 80ths, Bjarne Stroustrup and Margaret A. Ellis wrote their famous book Annotated C++ Reference Manual (ARM). These books served two purposes. First, there were many independent C++ implementations. ARM defined, therefore, the functionality of C++. Second, ARM was the base for the first C++ standard: C++98 (ISO/IEC 14882). C++98 had a few essential features: templates, the standard template library (STL) with its containers and algorithms, strings, and IO streams.

C++03

With C++03 (14882:2003), C++98 got a technical correction that is so small that there is no place on my timeline. In the community, C++03, which includes C++98, is called legacy C++.

Constrain your user-defined conversions -- Jonathan Mueller

jonathanmueller.pngSometimes you want to add an implicit conversion to a type. This can be done by adding an implicit conversion operator. For example, std::string is implicitly convertible to std::string_view.

Constrain Your User-Defined Conversions

By Jonathan Mueller

From the article:

Sometimes you want to add an implicit conversion to a type. This can be done by adding an implicit conversion operator. For example, std::string is implicitly convertible to std::string_view:

	class string { // template omitted for simplicity
	public:
	    operator std::string_view() const noexcept
	    {
	       return std::string_view(c_str(), size());
	    }
	}; 

The conversion is safe, cheap, and std::string and std::string_view represent the same platonic value — we match Tony van Eerd’s criteria for implicit conversions and using implicit conversions is justified.

However, even when all criteria are fulfilled, the conversion can still be dangerous.

Thread-Safe Queue - Two Serious Errors -- Rainer Grimm

concurrentarchitecture-grimm.pngIn my last post "Monitor Object"  I implemented a thread-safe queue. I made two serious errors. Sorry. Today, I will fix these issues.

Thread-Safe Queue - Two Serious Errors

by Rainer Grimm

From the article:

First, I want to show you again the erroneous implementation from my last post to understand the context.

// monitorObject.cpp

#include <condition_variable>
#include <functional>
#include <queue>
#include <iostream>
#include <mutex>
#include <random>
#include <thread>

class Monitor {
public:
    void lock() const {
        monitMutex.lock();
    }

    void unlock() const {
        monitMutex.unlock();
    }

    void notify_one() const noexcept {
        monitCond.notify_one();
    }

    template <typename Predicate>
    void wait(Predicate pred) const {                 // (10)
        std::unique_lock<std::mutex> monitLock(monitMutex);
        monitCond.wait(monitLock, pred);
    }
  
private:
    mutable std::mutex monitMutex;
    mutable std::condition_variable monitCond;
};

template <typename T>                                  // (1)
class ThreadSafeQueue: public Monitor {
public:
    void add(T val){
        lock();
        myQueue.push(val);                             // (6)
        unlock();
        notify_one();
    }
  
    T get(){
        wait( [this] { return ! myQueue.empty(); } );  // (2)
        lock();
        auto val = myQueue.front();                    // (4)
        myQueue.pop();                                 // (5)
        unlock();
        return val;
    }

private:
    std::queue<T> myQueue;                            // (3)
};


class Dice {
public:
    int operator()(){ return rand(); }
private:
    std::function<int()> rand = std::bind(std::uniform_int_distribution<>(1, 6),
                                          std::default_random_engine());
};


int main(){
  
    std::cout << '\n';
  
    constexpr auto NumberThreads = 100;
  
    ThreadSafeQueue<int> safeQueue;                      // (7)

    auto addLambda = [&safeQueue](int val){ safeQueue.add(val);          // (8)
                                            std::cout << val << " "
                                            << std::this_thread::get_id() << "; ";
                                          };
    auto getLambda = [&safeQueue]{ safeQueue.get(); };  // (9)

    std::vector<std::thread> addThreads(NumberThreads);
    Dice dice;
    for (auto& thr: addThreads) thr = std::thread(addLambda, dice());

    std::vector<std::thread> getThreads(NumberThreads);
    for (auto& thr: getThreads) thr = std::thread(getLambda);

    for (auto& thr: addThreads) thr.join();
    for (auto& thr: getThreads) thr.join();
  
    std::cout << "\n\n";
   
}

Object Ownership -- Ilya Doroshenko

objectownership-doroshenko.pngThis article goes over the spicy topic of object ownership. We covered the lifetime quirks, and we found out that manual memory management can be a nightmare, we new and delete in the correct order. There must be something better than that. Well, there is but it comes with its own can of worms.

Object Ownership

By Ilya Doroshenko

From the article:

Since we know the rules of new and delete, namely new allocates and delete destroys, we never really cared about who is responsible for the object. This caused a lot of confusion in the past. For instance, some API codes from Win32 return strings that should be LocalFree()d, like FormatMessage or GetEnvironmentStrings. POSIX, on the other hand, has strdup as a common example of you should free it yourself. This model is confusing because you may have a lot of return statements, before which you should always call free or delete, depending on the operation. However, we have RAII since the very beginning of C++, which adds constructors and destructors. So, in 1998 resourceful people decided to add auto_ptr to the standard.

The premise was simple:

  • a simple explicit constructor, that took raw pointer from new
  • destroyed by either an explicit release/reset or a destructor on the end of the block

This was the first attempt at a structured cleanup. As time passed, the initial point began to crumble. More issues arose and the question came up: Who owns the data?

 

 

Parsing Time Stamps Faster with SIMD Instructions -- Daniel Lemire

In software, it is common to represent time as a time-stamp string. It is usually specified by a time format string. Some standards use the format %Y%m%d%H%M%S meaning that we print the year, the month, the day, the hours, the minutes and the seconds. The current time as I write this blog post would be 20230701205436 as a time stamp in this format. It is convenient because it is short, easy to read and if you sort the strings lexicographically, you also sort them chronologically.

Parsing Time Stamps Faster with SIMD Instructions

by Daniel Lemire

From the article:

You can generate time stamps using any programming language. In C, the following program will print the current time (universal, not local time):

We are interested in the problem of parsing these strings. In practice, this means that we want to ...