Even the White House wants you to abandon C and C++, It’s time to focus on C++ safety and join the Bjarne initiative.

The C and C++ languages are no longer favored by the highest American authorities. Indeed, the White House wishes for developers to use memory-safe languages. In this report published on Monday, the Office of the National Cyber Director (ONCD) of the White House invites developers to reduce the risk of cyberattacks by using languages without memory vulnerabilities. IT companies “can prevent the introduction of multiple vulnerabilities into the digital ecosystem by adopting secure languages,” the presidency said in a statement. It refers to those that are protected against buffer overflow, out-of-bounds reads, and memory leaks.

From the report we can read:

Experts have identified a few programming
languages that both lack traits associated with memory safety and also have high proliferation
across critical systems, such as C and C++.Choosing to use memory safe programming languages
at the outset, as recommended by the Cybersecurity and Infrastructure Security Agency’s (CISA)
Open-Source Software Security Roadmap is one example of developing software in a secure-bydesign manner.

As C++ developers, we can’t ignore this significant issue. However, the C++ language is continuously improving, with new features introduced every three years. I’m confident that this issue could be addressed soon. Indeed, Bjarne the C++ creator is aware about the safety issues and work on this topic. He’s created a GitHub repository, and said about this repository:

“where people can put suggestions, and where I’m going to put my drafts and such, so that we can create a community working on getting this kind of stuff done in a reasonable time.”

C++ is a very powerful programming language, and it’s not trivial to replace it with another language, as Bjarne said:

“And anyway — which other language? The way I have seen it argued, we are going to have C++ replaced by about seven different languages, as of suggestions of about now. By the time it happens — 40 years from now — we’ll probably have 20 different ones, and they have to interoperate. This is going to be difficult.”

To get a concrete idea about the Bjarn proposition, you can refer to this video:

To resume, C++ is not a dead language. In fact, it remains widely used and actively developed across various industries and domains. While newer standards and frameworks have emerged over the years, C++ continues to be a popular choice for performance-critical applications, system software, game development, embedded systems, and other domains where efficiency, control, and low-level access are paramount. However, we need to address the safety issues soon to make C++ the most powerful and secure language in the world 🙂

Top 7 most used C++ idioms (Part2).

In the first part we discovered the RAII and Pimpl idioms, and in this second part, we will explore the CRTP, Copy-and-Swap, and Type Erasure idioms.

But before talking about these interesting idioms, let’s first discover the benefits of mastering C++ idioms and why it’s worth investing time to understand how they work.:

  1. Expressiveness: Idioms allow developers to express common design patterns and solutions concisely and clearly within the syntax of the language. By leveraging language-specific features and conventions, idioms can make code more readable and self-explanatory.
  2. Performance: Language-specific idioms often leverage language features optimized for performance. For example, in C++, idioms like RAII (Resource Acquisition Is Initialization) leverage constructors and destructors to manage resources efficiently, reducing the likelihood of resource leaks and improving performance.
  3. Safety and Reliability: Idioms can enforce best practices and safety measures specific to the language. For instance, in languages like Rust, ownership and borrowing idioms enforce memory safety at compile time, preventing common pitfalls such as null pointer dereferencing and data races.
  4. Compatibility: Idioms often align with the conventions and idiomatic style of the language community. Adhering to idiomatic practices makes code more compatible with existing libraries, frameworks, and coding standards, promoting interoperability and collaboration among developers.
  5. Maintainability: By following language-specific idioms and conventions, code becomes more consistent and predictable. This facilitates code maintenance, debugging, and refactoring, as developers familiar with the language can quickly understand and reason about idiomatic code.
  6. Performance Optimization: Idioms in languages like C++ and Rust enable performance optimizations by leveraging language features such as move semantics, compile-time polymorphism, and zero-cost abstractions. By using idiomatic constructs, developers can write efficient code without sacrificing readability or maintainability.
  7. Tooling Support: Language-specific idioms often benefit from dedicated tooling and static analysis tools tailored to the language’s syntax and semantics. These tools can help identify and enforce idiomatic patterns, detect potential issues, and provide automated refactorings to align code with best practices.

In summary, idioms in a specific programming language offer benefits such as expressiveness, performance, safety, compatibility, maintainability, performance optimization, and tooling support. By following idiomatic practices, developers can write code that is efficient, reliable, and easy to maintain within the context of the language ecosystem.

Let’s continue exploring our 7 idioms:

3- Curiously Recurring Template Pattern (CRTP)

The Curiously Recurring Template Pattern (CRTP) is an advanced template-based design pattern in C++. It is a technique that involves using inheritance and static polymorphism to achieve a form of compile-time polymorphism. Indeed the CRTP allows for compile-time optimization and inlining of code since the method calls are resolved at compile time. It also eliminates the runtime overhead associated with virtual function calls, making it suitable for performance-critical code.

The CRTP is commonly used in libraries and frameworks for tasks such as implementing mixin classes, static interfaces, and type-safe callback mechanisms. It is also used in some design patterns, such as the Curiously Recurring Template Singleton Pattern (CRSP), which employs CRTP to implement singletons.

Here’s a simple example demonstrating the CRTP:

template <typename Derived>
class Base {
public:
    void commonFunction() {
        // Base class implementation
        static_cast<Derived*>(this)->specificFunction(); // Call derived class method
    }

    // Virtual destructor to ensure correct destruction
    virtual ~Base() = default;
};

class DerivedClass : public Base<DerivedClass> {
public:
    void specificFunction() {
        // Derived class implementation
        // ...
    }
};

int main() {
    DerivedClass d;
    d.commonFunction(); // Calls both base and derived class methods
    return 0;
}

In this example, Base is a template class that serves as the base class, and DerivedClass inherits from Base with itself as the template argument. Base contains a method commonFunction() that calls specificFunction() defined in the derived class. When an instance of DerivedClass calls commonFunction(), it invokes both the base class’s and the derived class’s implementations.

To resume here’s a brief overview of how the CRTP works:

  1. Basic Structure: In the CRTP, a base class template is defined with a template parameter representing the derived class. The derived class then inherits from the base class and provides its own type as the template argument.
  2. Implementation: The base class template typically contains methods or members that depend on the derived class’s functionality. These methods or members can call methods defined in the derived class using static polymorphism.
  3. Usage: The derived class provides its own implementation of the methods required by the base class template. This allows the derived class to customize the behavior of the base class methods while still benefiting from the common functionality provided by the base class.

4-Copy-and-swap Idiom

The Copy and Swap idiom is a C++ programming technique used to implement the copy assignment operator and ensure strong exception safety guarantees. It involves swapping the contents of the current object with a copy of the object being assigned, thereby providing a robust and exception-safe way to perform assignments.

Here’s how the Copy and Swap idiom works:

  1. Define a Swap Function: First, you need to define a swap function for your class. This function swaps the internal state of the current object with the internal state of another object of the same type.
  2. Implement the Copy Assignment Operator: Implement the copy assignment operator (operator=) for your class. Instead of performing the assignment directly, create a copy of the right-hand side object, swap its contents with the current object using the swap function, and let the copy destructors clean up the swapped contents.
  3. Exception Safety: Since the swap operation and destruction of temporary objects are performed within the copy assignment operator, any exceptions thrown during the operation will leave the state of the current object unchanged. This provides the strong exception safety guarantee.

Here’s a basic example demonstrating the Copy and Swap idiom:

#include <algorithm> // For std::swap

class MyClass {
private:
    int* data;
    size_t size;

public:
    // Constructor
    MyClass(size_t size) : size(size), data(new int[size]) {}

    // Destructor
    ~MyClass() {
        delete[] data;
    }

    // Copy constructor
    MyClass(const MyClass& other) : size(other.size), data(new int[other.size]) {
        std::copy(other.data, other.data + size, data);
    }

    // Swap function
    friend void swap(MyClass& first, MyClass& second) noexcept {
        using std::swap;
        swap(first.data, second.data);
        swap(first.size, second.size);
    }

    // Copy assignment operator using Copy and Swap idiom
    MyClass& operator=(MyClass other) noexcept {
        swap(*this, other); // Swap contents with a copy of 'other'
        return *this;
    }
};

In this example, the swap function swaps the data and size members of two MyClass objects. The copy assignment operator takes advantage of the swap function by creating a copy of the right-hand side object (other), swapping its contents with the current object (*this), and returning a reference to the current object. This ensures that the assignment is both exception-safe and efficient.

However, In C++11 the new move feature. The copy-swap idiom and move semantics are both techniques used in C++ for managing the copying and moving of objects, but they serve different purposes and have different implementations.

  1. Copy-Swap Idiom:
  • The copy-swap idiom is a programming technique used to implement copy assignment operators in C++. It involves creating a copy of the object to be assigned, then swapping the contents of the current object with the copy.
  • The copy constructor creates a copy of the object to be assigned, ensuring that modifications to the original object do not affect the new object. The swap operation then exchanges the contents of the current object with the copied object.
  • This technique provides strong exception safety guarantees because the swap operation is typically implemented using no-throw operations.
  • While the copy-swap idiom ensures robustness and safety, it may incur unnecessary overhead if the object being copied is large or expensive to copy.
  1. Move Semantics:
  • Move semantics is a feature introduced in C++11 that allows objects to be moved from one location to another efficiently. It enables the transfer of resources, such as dynamically allocated memory or file handles, from temporary objects or rvalue references to other objects.
  • Unlike copying, moving involves transferring ownership of resources from one object to another without creating a copy. This can result in significant performance improvements, especially for large or expensive-to-copy objects.
  • Move semantics are implemented using move constructors and move assignment operators, which are used to efficiently transfer the resources of temporary objects or rvalue references.
  • Move semantics are particularly useful in situations where copying objects is expensive or unnecessary, such as when returning objects from functions or passing objects by value.

In summary, the copy-swap idiom and move semantics are complementary techniques used in C++ for managing object copying and moving. The copy-swap idiom provides strong exception safety guarantees but may incur overhead for large objects, while move semantics offer efficiency improvements by enabling the transfer of resources between objects without copying. Depending on the specific requirements and characteristics of the objects involved, developers can choose between these techniques to achieve the desired balance between safety and performance.

5-Type Erasure Idiom

Type erasure is a design pattern in C++ that allows for the implementation of polymorphic behavior without using virtual functions or inheritance. It enables the creation of generic interfaces and containers that can operate on objects of different types, providing type safety and flexibility. The type erasure idiom is particularly useful when dealing with heterogeneous collections or when the exact types of objects are not known at compile time.

The basic idea behind type erasure is to encapsulate objects of different types behind a common interface, hiding their specific types and allowing them to be treated uniformly. This is typically achieved using templates and dynamic polymorphism.

Here’s a high-level overview of how type erasure works in C++:

  1. Define a Conceptual Interface: Start by defining a conceptual interface that represents the common behavior or operations that objects of different types should support. This interface is often expressed using templates or abstract classes.
  2. Implement Type-Erased Wrapper: Create a type-erased wrapper class or template that can hold objects of different types but presents them through the common interface defined in step 1. This wrapper class usually uses dynamic memory allocation and polymorphism internally to store and manipulate objects of varying types.
  3. Use Type-Erased Objects: Use instances of the type-erased wrapper class in your code wherever polymorphic behavior is needed. Since the wrapper presents a uniform interface, you can call methods and access properties without knowing the exact underlying types.
  4. Dealing with Type Information: Optionally, provide mechanisms to query or retrieve type information from the type-erased objects, such as runtime type identification (RTTI) or custom mechanisms for type introspection.
  5. Benefits of Type Erasure: Type erasure provides flexibility and type safety by allowing heterogeneous collections and generic algorithms to be implemented without sacrificing performance or introducing complex inheritance hierarchies. It decouples the interface from the implementation, making it easier to maintain and extend codebases.

One common example of type erasure in C++ is the use of std::function from the C++ Standard Library. std::function can hold callable objects of different types (e.g., functions, lambdas, function objects) but presents them through a common interface, allowing them to be invoked uniformly.

Type erasure is a powerful and versatile technique in C++, but it may introduce some overhead due to dynamic memory allocation and virtual function calls. As with any design pattern, it’s essential to consider the trade-offs and choose the right approach based on the specific requirements and constraints of your application.

Here’s a simplified example of type erasure in C++, using a type-erased wrapper to store and manipulate objects of different types through a common interface:

#include <iostream>
#include <memory> // For std::unique_ptr

// Interface representing the common behavior for all types
class Shape {
public:
    virtual ~Shape() = default;
    virtual double area() const = 0;
};

// Concrete implementation of the Shape interface for a Circle
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    double area() const override {
        return 3.14159 * radius * radius;
    }
};

// Concrete implementation of the Shape interface for a Rectangle
class Rectangle : public Shape {
private:
    double width;
    double height;

public:
    Rectangle(double w, double h) : width(w), height(h) {}

    double area() const override {
        return width * height;
    }
};

// Type-erased wrapper class to hold objects of different types
class AnyShape {
private:
    // Pointer to the base class (Shape), allowing polymorphic behavior
    std::unique_ptr<Shape> ptr;

public:
    // Constructor taking any object that implements the Shape interface
    template<typename T>
    AnyShape(T shape) : ptr(std::make_unique<Model<T>>(std::move(shape))) {}

    // Delegate area() method call to the underlying object
    double area() const {
        return ptr->area();
    }

private:
    // Private inner class (model) to hold objects of any type
    template<typename T>
    class Model : public Shape {
    private:
        T shape;

    public:
        Model(T s) : shape(std::move(s)) {}

        double area() const override {
            return shape.area();
        }
    };
};

int main() {
    // Create instances of different shapes
    Circle circle(5.0);
    Rectangle rectangle(4.0, 6.0);

    // Create type-erased wrapper objects
    AnyShape anyCircle(circle);
    AnyShape anyRectangle(rectangle);

    // Call the area() method on the type-erased objects
    std::cout << "Circle area: " << anyCircle.area() << std::endl;
    std::cout << "Rectangle area: " << anyRectangle.area() << std::endl;

    return 0;
}

In this example, we have an interface Shape with a pure virtual function area(), which represents the common behavior for all shapes. We then have concrete implementations of the Shape interface for Circle and Rectangle.

The AnyShape class serves as a type-erased wrapper to hold objects of different types that implement the Shape interface. It uses template metaprogramming to create an inner class (Model) for each type that is stored. This inner class delegates the area() method call to the underlying object. Finally, in the main() function, we demonstrate creating instances of different shapes, wrapping them in AnyShape objects, and calling the area() method on them uniformly.

Top 7 most used C++ idioms (Part1).

Idioms and design patterns are both common solutions to recurring problems in software development, but they differ in scope, granularity, and formality:

  1. Scope:
    • Idioms: Idioms are small, language-specific coding techniques or patterns that address specific programming challenges within a particular programming language. They often involve leveraging language features or conventions to achieve a desired outcome efficiently and effectively.
    • Design Patterns: Design patterns are higher-level, language-agnostic architectural solutions to common design problems in software engineering. They provide general reusable templates for solving design issues and promoting best practices in software design.
  2. Granularity:
    • Idioms: Idioms tend to be more granular and focused on specific coding constructs or techniques within a single programming language. They often involve manipulating language features or syntax to achieve particular goals.
    • Design Patterns: Design patterns are more comprehensive and deal with broader design concepts and relationships between components within a software system. They provide templates for organizing and structuring code at a higher level of abstraction.
  3. Formality:
    • Idioms: Idioms are typically informal and are commonly passed down through experience, code reviews, or programming literature within a specific programming community. They may not always have formal names or documentation.
    • Design Patterns: Design patterns are more formalized and well-documented solutions to common design problems. They often have recognized names, descriptions, and implementation guidelines outlined in literature such as the Gang of Four (GoF) book “Design Patterns: Elements of Reusable Object-Oriented Software.”

In this post we will try to explore these common C++ idioms:

1- RAII

2-Pimpl

3- Curiously Recurring Template Pattern (CRTP)

4- Copy-and-swap

5- Type Erasure

6- Non-Virtual Interface (NVI)

7- SFINAE (Substitution Failure Is Not An Error)

The first part will focus on the RAII and PImpl idioms:

RAII(Resource Acquisition Is Initialization)

In C++, one of the most commonly used idioms is the RAII (Resource Acquisition Is Initialization) idiom. RAII is a powerful and widely adopted technique for managing resources such as memory, file handles, network connections, and locks in a deterministic and exception-safe manner. It ties the lifetime of resources to the lifetime of objects, ensuring that resources are properly acquired and released.

Here’s how RAII works and where it’s commonly used:

1-Managing Memory: RAII is extensively used in C++ for managing dynamic memory allocation using pointers, especially with std::unique_ptr and std::shared_ptr from the C++ Standard Library. These smart pointer classes ensure that memory is automatically deallocated when the object containing the pointer goes out of scope.

{
    std::shared_ptr<int> ptr(new int); // Resource acquired
    // Use ptr
} // Resource released automatically when ptr goes out of scope

2-File Handling: RAII is employed for managing file resources, ensuring that files are closed properly after use, even in the presence of exceptions or early returns.

{
    std::ifstream file("example.txt"); // Resource acquired
    // Read from file
} // Resource released automatically when file goes out of scope

3-Locking Mechanisms: RAII is used for managing locks to ensure that critical sections of code are properly synchronized and that locks are released when they are no longer needed.

{
    std::ifstream file("example.txt"); // Resource acquired
    // Read from file
} // Resource released automatically when file goes out of scope

4-Resource Management in Custom Classes: Developers often implement RAII in their own classes to manage custom resources such as database connections, network sockets, and GPU resources.

RAII promotes resource safety, reduces the likelihood of resource leaks and dangling pointers, and simplifies resource management by tying it to object lifetimes. It’s considered one of the cornerstones of modern C++ programming and is widely used throughout the language and its standard library.

Pimpl

The Pimpl idiom, which stands for “Pointer to Implementation,” is a C++ design technique used to hide the implementation details of a class from its users. It helps improve encapsulation, reduce compile-time dependencies, and minimize recompilation when implementation details change.

Here’s how the Pimpl idiom works:

  1. Separation of Interface and Implementation: With the Pimpl idiom, the public interface of a class is defined in its header file, while the private implementation details are encapsulated in a separate implementation file. This separation allows clients of the class to interact only with its public interface, hiding the complexity of the implementation.
  2. Pointer to Implementation: Instead of directly including the implementation details in the class declaration, the class contains a pointer to an opaque (i.e., forward-declared) implementation class. This pointer is typically declared as a private member of the class.
  3. Forward Declaration: Since the implementation details are hidden from the class’s users, only the declaration of the implementation class is needed in the header file. This is achieved using a forward declaration (class Impl;), avoiding the need to include the implementation details in the header.
  4. Reduced Compilation Dependencies: By separating the interface and implementation and using forward declarations, changes to the implementation details (e.g., adding, removing, or modifying private members) do not require recompilation of the code that uses the class’s interface. This can significantly reduce build times, especially in large codebases.
  5. Dynamic Memory Allocation: Typically, the implementation class is allocated dynamically on the heap using new, and the pointer to it is managed by the public class. This allows for dynamic polymorphism and enables the use of the “bridge” and “strategy” design patterns.

Here’s a simplified example demonstrating the Pimpl idiom:

// MyClass.h (Header file)
#pragma once

class MyClass {
public:
    MyClass();
    ~MyClass();
    
    void doSomething();
    
private:
    class Impl; // Forward declaration
    
    Impl* pImpl; // Pointer to implementation
};
// MyClass.cpp (Implementation file)
#include "MyClass.h"

class MyClass::Impl {
public:
    void doInternalWork() {
        // Implementation details...
    }
};

MyClass::MyClass() : pImpl(new Impl()) {}

MyClass::~MyClass() {
    delete pImpl;
}

void MyClass::doSomething() {
    pImpl->doInternalWork();
}

By employing the Pimpl idiom, the implementation details of MyClass are hidden from users of the class, and changes to the implementation can be made without affecting client code or requiring recompilation of dependent files. This improves modularity, maintainability, and build times in C++ projects.

Top 7 most used C++ Idioms (Part 2)

Are you curious to know where the move feature is used in your C++ projects, on your behalf?

Move semantics is a feature introduced in C++11 that allows more efficient transfer of resources (such as dynamic memory) from one object to another. It addresses the inefficiencies associated with deep copying objects, especially large ones, by allowing objects to “steal” the resources of other objects when possible, rather than duplicating them.

Before C++11, when you assigned one object to another, a copy constructor or assignment operator would be invoked, resulting in a deep copy of the object’s data. This process could be expensive, particularly for large objects or those containing dynamic memory allocations. This feature is particularly useful in scenarios where performance optimization is critical, such as in high-performance computing, game development, and resource-constrained environments.

Move semantics introduces the notion of “rvalue references” and a new concept called “move constructors” and “move assignment operators”.

Here’s how it works:

  1. Rvalue References (&&): Rvalue references allow you to bind to temporary objects (rvalues), which are often objects that are going to be destroyed or not needed anymore. They are denoted by the double ampersand &&.
  2. Move Constructors: A move constructor is a special constructor that takes an rvalue reference to another object of the same type, and “moves” the resources from that object into the new object. It does this by “stealing” the internal pointers or handles from the source object and nullifying them, leaving the source object in a valid but unspecified state. This is typically done using the std::move() function, which converts an lvalue into an rvalue.
  3. Move Assignment Operators: Similar to move constructors, move assignment operators allow the efficient transfer of resources between objects. They take an rvalue reference to another object of the same type and perform a move assignment operation, leaving the source object in a valid state.

However, do I need to master this feature to benefit from its significant performance added value? Fortunately, in many cases, the compiler does an amazing job on your behalf and uses this feature when it can do so.

Let’s take this basic sample:

class test
{

};
test f()
{
    test t;
    return t;
}

In this sample, the move feature is not explicitly used by the developer, but it is used implicitly by the compiler. Indeed, when a function returns a value by value, traditionally, a copy of the object is made, which involves allocating memory for the copy and then copying the contents of the original object to the newly allocated memory. However, with move semantics, instead of making a copy, the resources owned by the local object within the function are transferred to the object being returned, if possible.

How to detect where the compiler use this feature on your behalf?

1- Using clang -cc1 -ast-dump command

The Clang Abstract Syntax Tree (AST) dump command is a powerful tool provided by the Clang compiler front-end for C, C++, and Objective-C. It allows developers to inspect the internal representation of their source code as an Abstract Syntax Tree, which is a hierarchical representation of the structure of the source code.

Let’s execute this command on our basic sample

|-CXXRecordDecl 0x1ccdf79b618 <test.cpp:1:1, line:4:1> line:1:7 referenced class test definition
| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr
| | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
| | |-MoveConstructor exists simple trivial
| | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment exists simple trivial needs_implicit
| | `-Destructor simple irrelevant trivial needs_implicit
| |-CXXRecordDecl 0x1ccdf79b738 <col:1, col:7> col:7 implicit class test
| |-CXXConstructorDecl 0x1ccdf888b58 <col:7> col:7 implicit used constexpr test 'void () noexcept' inline default trivial
| | `-CompoundStmt 0x1ccdf889098 <col:7>
| |-CXXConstructorDecl 0x1ccdf888cc8 <col:7> col:7 implicit constexpr test 'void (const test &)' inline default trivial noexcept-unevaluated 0x1ccdf888cc8
| | `-ParmVarDecl 0x1ccdf888de8 <col:7> col:7 'const test &'
| `-CXXConstructorDecl 0x1ccdf888ec8 <col:7> col:7 implicit used constexpr test 'void (test &&) noexcept' inline default trivial
|   |-ParmVarDecl 0x1ccdf888fe8 <col:7> col:7 'test &&'
|   `-CompoundStmt 0x1ccdf8892a0 <col:7>
`-FunctionDecl 0x1ccdf8889d8 <line:5:1, line:9:1> line:5:6 f 'test ()'
  `-CompoundStmt 0x1ccdf8892f8 <line:6:1, line:9:1>
    |-DeclStmt 0x1ccdf8891f0 <line:7:5, col:11>
    | `-VarDecl 0x1ccdf888ad8 <col:5, col:10> col:10 used t 'test':'test' nrvo callinit
    |   `-CXXConstructExpr 0x1ccdf8891c8 <col:10> 'test':'test' 'void () noexcept'
    `-ReturnStmt 0x1ccdf8892e0 <line:8:5, col:12>
      `-CXXConstructExpr 0x1ccdf8892b0 <col:12> 'test':'test' 'void (test &&) noexcept'
        `-ImplicitCastExpr 0x1ccdf889228 <col:12> 'test':'test' xvalue <NoOp>
          `-DeclRefExpr 0x1ccdf889208 <col:12> 'test':'test' lvalue Var 0x1ccdf888ad8 't' 'test':'test'

As we can see the compiler generate for the class test the Move constructor and the Move assignement

| | |-MoveConstructor exists simple trivial
| | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment exists simple trivial needs_implicit

And for the return statement of the function f() the move constructor is called.

 `-ReturnStmt 0x1ccdf8892e0 <line:8:5, col:12>
      `-CXXConstructExpr 0x1ccdf8892b0 <col:12> 'test':'test' 'void (test &&) noexcept'
        `-ImplicitCastExpr 0x1ccdf889228 <col:12> 'test':'test' xvalue <NoOp>
          `-DeclRefExpr 0x1ccdf889208 <col:12> 'test':'test' lvalue Var 0x1ccdf888ad8 't' 'test':'test'

Thanks to the C++11 standard and modern compilers, they utilize the move feature in each case where it’s trivial to use, bringing added value in performance compared to C++ compilers before the C++11 standard.

2-Using CppDepend

CppDepend internally uses Clang to parse the source code, traverse the AST to obtain the maximum of useful data needed to calculate metrics, and detect the structure and dependencies of your codebase.

For the class test we can see the generated move constructor in the CppDepend class browser:

And to know where it’s used, we can just right click on the move constructor and select the methods using it

And here’s the result:

As we can see the function f() is using implicitly the move contructor.

CppDepend get the AST from clang and convert it to a suitable model that we can query using our code query language:

If you are curious to know where the move feature is used in your C++ projects, you can use one of these two possibilities to know more your codebase 🙂

C++ is now a feature-rich language, Be aware of OverEngeniering

Being aware of overengineering is crucial when working with a feature-rich language like C++. Overengineering occurs when developers introduce overly complex or unnecessary solutions to a problem.

C++ developers could be attempted to use as possible the new features introduced by the new standards. which makes the code finally more complicated than it must be.

Here’s an example to show how C++ metaprogramming can be used to create a type-erased container with arithmetic operations that are evaluated at compile time. While this example show the power and flexibility of C++ metaprogramming techniques. it might seem complicated due to the use of templates, concepts and constexpr functions:

#include <iostream>
#include <type_traits>

template<typename T>
concept Arithmetic = std::is_arithmetic_v<T>;

template<Arithmetic T>
struct AnyType {
    constexpr AnyType(const T& value) : value_(value) {}

    template<Arithmetic U>
    constexpr auto add(const AnyType<U>& other) const {
        return AnyType{ value_ + other.value_ };
    }

    template<Arithmetic U>
    constexpr auto subtract(const AnyType<U>& other) const {
        return AnyType{ value_ - other.value_ };
    }

    template<Arithmetic U>
    constexpr auto multiply(const AnyType<U>& other) const {
        return AnyType{ value_ * other.value_ };
    }

    template<Arithmetic U>
    constexpr auto divide(const AnyType<U>& other) const {
        static_assert(other.value_ != 0, "Division by zero");
        return AnyType{ value_ / other.value_ };
    }

    template<Arithmetic U>
    friend std::ostream& operator<<(std::ostream& os, const AnyType<U>& any) {
        return os << any.value_;
    }

private:
    T value_;
};

int main() {
    constexpr AnyType<int> x{ 5 };
    constexpr AnyType<float> y{ 2.5f };

    constexpr auto addition = x.add(y);
    constexpr auto subtraction = x.subtract(y);
    constexpr auto multiplication = x.multiply(y);
    constexpr auto division = x.divide(y);

    std::cout << "Addition: " << addition << std::endl;
    std::cout << "Subtraction: " << subtraction << std::endl;
    std::cout << "Multiplication: " << multiplication << std::endl;
    std::cout << "Division: " << division << std::endl;

    return 0;
}

In this example:

  • We define a concept Arithmetic to constrain the template parameters to arithmetic types.
  • The AnyType class template is defined to hold any arithmetic type.
  • We provide member functions (add, subtract, multiply, divide) that perform arithmetic operations between AnyType objects of potentially different types.
  • We use constexpr to ensure that these operations are evaluated at compile time.
  • We overload the operator<< to allow streaming AnyType objects to std::ostream.

Yes sometimes for specific needs we can develop a code like that, But in general do we need this sophisticated class to do basic arithmetic operations, it’s like construct a tank to kill a fly 🙂

To resume try to not be tempted by the sophiscated new features and try to use them only if you need to. And always try to Follow KISS and YAGNI Principles:

Keep It Simple, Stupid (KISS) and You Ain’t Gonna Need It (YAGNI) are principles that advocate for simplicity and avoiding unnecessary features until they are needed.

Proposal for the next C++ standards

Modern C++ refers to the evolution of the C++ programming language beyond its original design principles, incorporating newer features, idioms, and best practices that have emerged since the language’s inception.

C++ is standardized by the International Organization for Standardization (ISO). C++ standards are typically released every few years, bringing new features and improvements to the language.

Modern C++ introduces a range of new features aimed at improving code clarity, safety, and performance. The smart pointers was a great added feature. Indeed, they provides safer and more convenient memory management compared to raw pointers.

And the smart pointers are now popular and widely used in the C++ projects. Just a quick look in the most popular C++ projects in Github proof that the smart pointers are the prefered way to work with pointers.

However, the raw pointers still there and many developers still use them. So why not assist them to use the smart pointers by forcing using the smart pointers by the compiler. The proposal consist of adding a new attribute to specify that a class can’t use raw pointers as fields types or method params types.

This way the compiler will protect the class code from the raw pointers 🙂

Even more I think it’s beneficial to the C++ developers to add the [[modern]] attribute to qualify that a class is modern.

And the compiler will reject the bad practices from the legacy C++.

Another suggestion concerns the decalaration of the shared pointers. Indeed, the smart pointers are now widly used, so why not provide a shortcut of declaring the shared pointer and instead of typing:

std::shared_ptr<Test> ptr_name;

only type:

ref Test ptr_name;

While this proposal may not introduce significant enhancements to the language, it is imperative to establish mechanisms that allow the compiler to safeguard the code against the legacy pitfalls of C++ programming.

Average experience of active C++ development Poll

C++ has a steep learning curve compared to some other programming languages. Its syntax and concepts can be complex, especially for beginners or those transitioning from higher-level languages. C++ may not be as beginner-friendly as some other languages, it offers unparalleled power, flexibility, and opportunities for those willing to invest in learning and mastering its intricacies. 

The level of experience, particularly with active development spanning more than five years, often correlates with a deeper understanding of C++ and its capabilities. Developers who have been actively working with C++ for an extended period tend to have a comprehensive knowledge of the language’s features, nuances, and ecosystem, enabling them to leverage its power effectively in various applications.

As developers gain experience in C++, they may indeed transition into managerial roles as they progress in their careers. In these roles, they may spend a significant amount of their time managing teams, projects, and resources rather than actively writing code themselves. This transition is common in many industries and reflects the natural career progression of experienced developers.

Maybe launching a poll to gather data on the level of active involvement of C++ developers with over 5 years of experience in the development stage would be a great way to gain insights into their career trajectories and preferences.

How many years have you been actively developing in C++?

Loki: The Premier C++ Library for Mastering Design Patterns – Join Us in Its Modernization!

Should you opt to delve into modern C++ design with a background in Object-Oriented Programming (OOP), consider starting your journey with the Loki library.

Loki is a C++ software library authored by Andrei Alexandrescu, developed alongside his book “Modern C++ Design.”

In the C++ community, there’s a need for a library that provides a robust framework for design patterns. Loki stands out as a strong contender for such a role. Therefore, modernizing Loki and incorporating as many widely used design patterns as possible would greatly benefit C++ developers.

Continue reading “Loki: The Premier C++ Library for Mastering Design Patterns – Join Us in Its Modernization!”

Exciting News for C++ Enthusiasts: The Modern C++ Challenge by CoderGears!

Hey Coders!

We’re thrilled to announce an exciting opportunity for C++ developers – The Modern C++ Challenge, organized by CoderGears! This contest is all about embracing innovation and showcasing your skills in modern C++ development.

Objective: The goal is simple yet stimulating – modernize the Loki library using the newest C++ features. Loki, a C++ library crafted by Andrei Alexandrescu, is known for its extensive use of template metaprogramming and offers tools like typelist, functor, singleton, and more. Your mission is to revitalize Loki, not to replace existing libraries like Boost, but to help the community adopt the latest C++ standards.

Continue reading “Exciting News for C++ Enthusiasts: The Modern C++ Challenge by CoderGears!”

Top 10 C++ Libraries in 2024

C++ is a powerful programming language that provides a wide range of libraries to help developers build efficient and effective software. These libraries cover a variety of domains, including general-purpose programming, graphics, networking, databases, and more.

However, be careful choosing a software library is not always an easy task, specially if many concurrent libraries exist for a specific need. Many factors could influence a developement team to choose a library, here are some of them:

Continue reading “Top 10 C++ Libraries in 2024”