Articles & Books

C++ programmer's guide to undefined behavior: part 12 of 11

Your attention is invited to the final part of an e-book on undefined behavior. This is not a textbook, as it's intended for those who are already familiar with C++ programming. It's a kind of C++ programmer's guide to undefined behavior and to its most secret and exotic corners. The book was written by Dmitry Sviridkin and edited by Andrey Karpov. Why is it the chapter 12 of 11? We couldn't resist highlighting the favorite error of C and C++ programmers that even has its own name—the Off-by-one Error.

C++ programmer's guide to undefined behavior: part 12 of 11

by Dmitry Sviridkin

From the article:

LLVM can generate the ud2 instruction on x86, which is an invalid instruction, often used as an indicator of unreachable code. If the program tries to execute it, it'll crash with the SIGILL signal. The code that causes undefined behavior can be marked as unreachable and replaced with ud2 or discarded in the future. In our wonderful example, the compiler is fully aware that buffer.size() == 0, and it hasn't been changed.

 

Learning to read C++ Compiler Errors: Failing to Create a shared_ptr -- Raymond Chen

Depositphotos_478507824_S.jpgThe trick to understanding C++ compiler error messages is to focus on two things. First, look at the beginning of the error message, which tells you what went wrong at a very low level. Then skip over the intermediate errors that follow the chain of calls until you end up at the line of code that you wrote. That original line of code is the one that is leading the compiler to a bad place. After that, you sometimes get supplemental information that helps you understand the low-level error better.

Learning to read C++ Compiler Errors: Failing to Create a shared_ptr

by Raymond Chen

From the article:

Consider the following erroneous code:

#include <memory>
#include <string>

struct WidgetOptions
{
    // imagine there is interesting stuff here
};

struct Widget
{
    Widget(WidgetOptions const* options);

    // imagine there is other interesting stuff here
};

void oops()
{
    WidgetOptions options;

    // The next line fails to compile
    std::shared_ptr<Widget> widget =
        std::make_shared<Widget>(options);
}

Here comes the error explosion.

// gcc
In file included from bits/stl_tempbuf.h:61,
                 from memory:66,
                 from sample.cpp:1:
bits/stl_construct.h: In instantiation of 'void std::_Construct(_Tp*, _Args&& ...) [with _Tp = Widget; _Args = {WidgetOptions&}]':
bits/alloc_traits.h:657:19:   required from 'static void std::allocator_traits<std::allocator<void> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = Widget; _Args = {WidgetOptions&}; allocator_type = std::allocator<void>]'
  657 |         { std::_Construct(__p, std::forward<_Args>(__args)...); }
      |           ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bits/shared_ptr_base.h:607:39:   required from 'std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args = {WidgetOptions&}; _Tp = Widget; _Alloc = std::allocator<void>; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
  607 |           allocator_traits<_Alloc>::construct(__a, _M_ptr(),
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
  608 |               std::forward<_Args>(__args)...); // might throw
      |               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
bits/shared_ptr_base.h:969:16:   required from 'std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = Widget; _Alloc = std::allocator<void>; _Args = {WidgetOptions&}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
  969 |           auto __pi = ::new (__mem)
      |                       ^~~~~~~~~~~~~
  970 |             _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bits/shared_ptr_base.h:1713:14:   required from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<void>; _Args = {WidgetOptions&}; _Tp = Widget; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
 1713 |         : _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...)
      |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bits/shared_ptr.h:463:59:   required from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<void>; _Args = {WidgetOptions&}; _Tp = Widget]'
  463 |         : __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...)
      |                                                                  ^
bits/shared_ptr.h:1007:14:   required from 'std::shared_ptr<typename std::enable_if<(! std::is_array<_Tp>::value), _Tp>::type> std::make_shared(_Args&& ...) [with _Tp = Widget; _Args = {WidgetOptions&}; typename enable_if<(! is_array<_Tp>::value), _Tp>::type = Widget]'
 1007 |       return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
      |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1008 |                              std::forward<_Args>(__args)...);
      |                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sample.cpp:22:33:   required from here
   22 |         std::make_shared<Widget>(options);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
bits/stl_construct.h:119:7: error: no matching function for call to 'Widget::Widget(WidgetOptions&)'
  119 |       ::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sample.cpp:11:5: note: candidate: 'Widget::Widget(const WidgetOptions*)'
   11 |     Widget(WidgetOptions const* options);
      |     ^~~~~~
sample.cpp:11:33: note:   no known conversion for argument 1 from 'WidgetOptions' to 'const WidgetOptions*'
   11 |     Widget(WidgetOptions const* options);
      |            ~~~~~~~~~~~~~~~~~~~~~^~~~~~~
sample.cpp:9:8: note: candidate: 'constexpr Widget::Widget(const Widget&)'
    9 | struct Widget
      |        ^~~~~~
sample.cpp:9:8: note:   no known conversion for argument 1 from 'WidgetOptions' to 'const Widget&'
sample.cpp:9:8: note: candidate: 'constexpr Widget::Widget(Widget&&)'
sample.cpp:9:8: note:   no known conversion for argument 1 from 'WidgetOptions' to 'Widget&&'
Compiler returned: 1
 

Replace CRTP with Concepts -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGThis article explores how C++20 concepts can replace CRTP when implementing static interfaces for a family of classes. By leveraging concepts, we achieve cleaner, more readable, and less error-prone code—provided you have access to C++20.

Replace CRTP with Concepts

by Sandor Dargo

From the article:

In my Meeting C++ 2024 trip report, among my favourite ideas I mentioned Klaus Iglberger’s talk where he mentioned the possibility of replacing the curiously returning template pattern with the help of class tagging and concepts.

Class tagging might mean different things in different contexts, or at least might be implemented in different ways. The end goal is to mark, in other words, tag classes or functions to be used in certain contexts, with certain algorithms. As you’ll see, in our case it’ll also be a tool to prevent duck typing.

We are going to see an example implementation of a static interface with CRTP with a couple of different derived classes, then we’ll see the implementation without CRTP.

The CRTP solution

With the static interface, we are creating a static family of types. There is no need for dynamic polymorphism to share the same interface. It’s still granted through a base class, which is a template taking the deriving class as a parameter.

 

 

Solving the Puzzle of Trying to Put an Object into a std::optional -- Raymond Chen

RaymondChen_5in-150x150.jpgLast time, we investigated the puzzle of why the compiler wouldn’t let us put an object into a std::optional. It came down to the fact that the object is not copy-constructible, move-constructible, copy-assignable, or move-assignable, so there’s no way to put the temporary object into the std::optional.

Solving the Puzzle of Trying to Put an Object into a std::optional

by Raymond Chen

From the article:

What we have to do is construct the object in place inside the std::optional. And the C++ standard library term for “construct an object inside a container” is “emplace”.

struct Doodad
{
    Doodad();
    ~Doodad();
    std::unique_ptr<DoodadStuff> m_stuff;
};

struct Widget
{
    std::optional<Doodad> m_doodad;

    Widget()
    {
        if (doodads_enabled()) {
            m_doodad.emplace();
        }
    }
};

The parameters to emplace are whatever parameters you would have passed to the Doodad constructor. In our case, we wanted the default constructor, so that means that we pass nothing to emplace().

Court is in session: Top 10 most notorious C and C++ errors in 2024

Every year, we witness the same drama: bugs wreak havoc on our code as if asserting their dominance. But today, the tide turns—it's time for judgment. Let's dive into the most intriguing bugs we've uncovered this year.

Court is in session: Top 10 most notorious C and C++ errors in 2024

by Aleksandra Uvarova

From the article:

The seventh defendant, what are you draggin' behind your back? Show us quickly! Ah! A little thief! Decided to pocket a juicy chunk of code, huh? No chance, we're bringing this to the light. Honestly, we really wanted to paste the full code of the function here—which has almost 400 code lines—to make searching for errors more interesting. However, we don't want to abuse your mouse wheel, so we've included just the most intriguing part.

Write More C++ Code Thanks to constexpr -- Andreas Fertig

me.pngSince its introduction, constexpr in C++ has evolved significantly, offering powerful ways to optimize code at compile-time. This article shares a real-world story of using constexpr to dramatically reduce memory usage and code size in an embedded system, showcasing its potential to improve both performance and efficiency.

Write More C++ Code Thanks to constexpr

by Andreas Fertig

From the article:

I'm a big fan of constexpr and am not alone. Jason Turner is also very vocal, having coined the term "constexpr all the things".

Well, demonstrating the powers of constexpr is nonetheless something difficult. I know that from my training classes and various consulting contracts. Today, I'd like to share a story from back in time when a customer hired me to consult. They did develop an embedded system and ran out of memory. Not during run-time, but before. The features they wanted to put in the chip were too big in code size and somewhat RAM.

Initial constexpr-free example

They used a class I've seen a couple of times in embedded systems with some variations. A string brings its memory picky-back.

It’s just β€˜,’ – The Comma Operator -- Coral Kashri

We all know that every ‘,’ matters in this language, so I decided to talk directly about that character today. So, how much impact can be for such a small little character?

It’s just ‘,’ – The Comma Operator

by Coral Kashri

From the article:

This operator comes from C, where it tells the compiler to evaluate all the expressions (left to right) and to return the result of the latest evaluated expression. For example:
     int a, b;
     a = 5, b = 4, b += a, ++a, std::cout << b << " " << a; // Prints 9 6

Another example of that operator usage is as follows:

     for (size_t i = 0, k = 500; i < 10; ++i, ++k) { /*...*/ }
 
We can see this operator in action in the third section of the for statement. It evaluates the ++i and then evaluates ++k.

How to Ensure a Class is not Copyable or Movable -- Sandor Dargo

Dargo-classisnotcopyable.pngThe topic of this post is to show different ways to ensure that a class is either non-moveable or non-copyable.

How to Ensure a Class is not Copyable or Movable

by Sandor Dargo

From the article:

If we follow the classification proposed by Sebastian Theophil, we can talk about 4 different class types:

  • value classes
  • container classes
  • resource classes
  • singleton classes

While the first two should be regular classes offering both copy and move semantics, the latter two are different. One shouldn’t be able to copy resources and singletons probably shouldn’t be moveable.

It’s up to us to ensure that a class we create implements the right special member functions (SMFs from now on). And the Hinnant table is here to guide us.

In C++, How Can I Make a Default Parameter be the This Pointer of the Caller? -- Raymond Chen

RaymondChen_5in-150x150.jpgIn C++, associating member objects like properties or events with their containing class often requires passing this redundantly. This article explores a generalized, flexible solution using templates, variadic arguments, and deducing this to streamline ownership initialization without boilerplate.

In C++, How Can I Make a Default Parameter be the This Pointer of the Caller? Revisited

by Raymond Chen

From the article:

Some time ago, we looked at making the default parameter of a method be the this pointer of the caller. The scenario was something like this:

struct Property
{
    Property(char const* name, int initial, Object* owner) :
        m_name(name), m_value(initial), m_owner(owner) {}

    ⟦ other methods elided - use your imagination ⟧

    char const* m_name;
    Object* m_owner;
    int m_value;
};

struct Widget : Object
{
    Property Height{ "Height", 10, this };
    Property Width{ "Width", 10, this };
};

and we didn’t want to have to type this as the last parameter to all the Property constructors. We came up with this:

template<typename D>
struct PropertyHelper
{
    Property Prop(char const* name, int initial)
    { return Property(name, initial, static_cast<D*>(this)); }
};

struct Widget : Object, PropertyHelper<Widget>
{
    Property Height = Prop("Height", 10);
    Property Width = Prop("Width", 10);
};

The Operations for Reading and Writing Single Elements for C++ Standard Library Maps -- Raymond Chen

RaymondChen_5in-150x150.jpgSome time ago, I noted that the std::map subscript operator is an attractive nuisance. It is the most convenient syntax, but is not often what you actually want.

The Operations for Reading and Writing Single Elements for C++ Standard Library Maps

by Raymond Chen

From the article:

I’ve broken down the various std::map lookup and update operations into a table so you can choose the best one for your situation.
2024-11-21_13-35-17.png

In the table above, key is the map key, value is the mapped type, and params are parameters to the mapped type constructor.

Note that insert and the first emplace¹ take a value which is discarded if it turns out that the key already exists. This is undesirable if creating the value is expensive.

One frustrating scenario is the case where the mapped type’s default constructor is not the constructor you want to use for operator[], or if you want the initial mapped value to be the result of a function call rather than a constructor. Here’s something I sort of threw together.