Tag Archives: C++

Thoughts about getters and setters in C++

This article deals with getters and setters in C++. I am sorry, it is not about coroutine, but part 2 of thread pools will come in the following weeks.

TL;DR: Getters and setters are bad for structure like objects.

Introduction

In this article, I will only give my opinion about them, I don’t want to offend anyone, I am just going to explain why, or why not, use getters and setters. I would be glad to have any debates in the comments part.

Just to be clear about what I am talking about when I talk about getter, I talk about a function that just returns something, and when I talk about setter, I talk about a function that just modifies one internal value, not doing any verification or other computations.

Getter performances

Let’s say we have a simple structure with their usual getter and setters:

Let’s compare this version with the one without getters and setters.

It is incredibly shorter and less error_prone. We can not make the error to return the last name instead of the first name.

Both codes are fully functional. We have a class Person with the first name, the last name, and an age. However, let’s say we want a function that returns the presentation of a person.

Show the performance differences between computation using getters by value and public access.

The version without getters performs this task 30% quicker than the version with getters. But why? It is because of the return by value of the getters functions. Returning by value makes a copy that results in poorer performance. Let’s compare the performance between person.getFirstName(); and person.firstName.

Show the performance differences between getters by value and public access.

As you can see, accessing directly the first name instead of a getter is equivalent to a noop.

Getter by const reference

However, it is possible to not return by value but return by reference instead. Going that way, we will have the same performance as without getters. The new code will look like that

Since we get the same performance as before, are we done? To answer this question, you can try to execute this code.

You may see some weird characters wrote in the console. But why? What happened when you do make().getLastName()?

  1. You create a Person
  2. You get a reference to the last name
  3. You delete Person

You have a dangling reference! It can lead to a crash (in the best case) or something worst than what can be found in a horror movie.

To deal with such a thing, we must introduce reference qualified functions.

Here is the new solution that works everywhere. You need 2 getters. One for lvalue and one for rvalue (both xvalue and prvalue).

Setter issues

There is not a lot to say in this section. If you want to achieve the best performances, you must write a setter that takes a lvalue and one that takes a rvalue. However, it is generally fine to just have a setter that takes a value that will be moved. Nevertheless, you have to pay the price of an extra move. However, that way, you cannot just make a little enhancement. You must replace the whole variable. If you just wanted to replace one A by a D in a name, it will not be possible by using setters. However, using direct access makes it possible.

What about immutable variables?

Ones will tell you to just make the member attribute as const. However, I am not ok with this solution. Indeed, making it const will prevent the move semantic and will lead to unnecessary copy.

I have no magic solution to propose you right now. Nevertheless, we can write a wrapper which we can named immutable<T>. This wrapper must be able to be :

  1. Constructible
  2. Since it is immutable, it must not be assignable
  3. It can be copy constructible or move constructible
  4. It must be convertible to const T& when being a lvalue
  5. It must be convertible to T when being a rvalue
  6. It must be used like other wrapper through operator* or operator->.
  7. It must be easy to get the address of the underlying object.

Here is a little implementation

So, for an immutable Person, you can just write:

Conclusion

I would not say that getters and setters are bad. However, when you do not need to do anything else in your getter and your setter, achieving the best performance, safety and flexibility lead to writing:

  • 3 getters (or even 4): const lvalue, rvalue, const rvalue, and if you want, non-const lvalue (even if sounds really weird since it is easier to just use direct access)
  • 1 setter (or 2 if you want to have the maximum performance)

It is a lot of boilerplate for almost anything.

Some people will tell you that getters and setters enforce encapsulation, but they don’t. Encapsulation is not just making attributes private. It is about hiding things from users, and for structure-like objects, you rarely want to hide anything.

My advice is: when you have a structure like objects, just don’t use getter and setters and go with public / direct access. To be simple, if your setter does not enforce any invariant, you don’t need a private attribute.

PS: For people who use libraries using shallow copy, the performance impact is less important. However, you still need to write 2 functions instead of 0. Don’t forget, the less code you write, the less bugged it will be, easier to maintain, and easier to read.

And you ? Do you use getters and setters? And why?

Thread pool with coroutines: Threads (1/3)

Introduction

In this little series of articles, we are going to see how to implement a thread pool usable with coroutines. This series will contain these articles :

  1. Creating a Thread
  2. Creating the pool
  3. Using future with the pool

The final objective will be to be able to write something like that:

Choice of implementation for the thread pool

We will use the well-known work-stealing algorithm inside our thread pool. It implies that each thread has its own task queue and threads can steal tasks from each other. It will lead to concurrency with shared variables between threads, hence, we must be careful with data races.

To deal with data races, I decided to make some homemade helpers inspired by the Rust programming language.

Mutex

Here is the first helper I have made. A mutex protects a resource from data race. So we can make a template class to protect the template argument. We use a callback to operate on a protected variable.

Why do I use a shared mutex? I use a shared mutex because multiple readers are not an issue.

Condition variable

What are the events that can occured within a thread?

  1. The thread can be requested to stop
  2. The thread can have a new task to perform

To not use CPU resources when the thread is not fed (i.e, there is no task to run), I decided to use a condition variable. The idea is simple, on the waiting thread, you wait for an event, and you go out of the wait function when the predicate is satisfied, and in another thread, you notify the condition variable to wake up.

Since a condition variable is generally used with a Mutex, I decided to join them together through inheritance. Hence, a condition variable behaves like a mutex but can be waited on also.

You may wonder what is std::stop_token, it is simply a C++20 feature provided by std::jthread that avoid user to wait on an atomic boolean. Put it simply, a std::jthread, when it is destroyed, do two things:

  1. It calls request_stop to a std::stop_source that will notify the std::stop_token
  2. It joins the thread

An Awaiter

With coroutines, the task will not be a function, but a coroutine_handle which will be resumed. Hence, we need to have an object that manages this handle.

One will observe that we destroy the coroutine only if it was not resumed. It is a movable only type.

A thread safe queue

Now that we have our Awaiter object, we must push them into a thread-safe queue. The new tasks will be pushed into the queue, and the thread pool will pop them one by one.

Since the queue may be empty, the pop operation can return nothing, represented by a std::nullopt.

We have 3 operations possible.

  1. Push: this operation enqueue a new task and notify the condition variable
  2. Pop: This operation deque a task to be executed in the current thread.
  3. Wait for an element: This operation will make the current thread idle until we got a new task (notified by the push function)

The thread

It is time to design our thread class.

The thread class will be designed over the std::jthread class. It will also embed a thread-safe queue of Awaiters.

Thus, we can lay:

First, we can imagine what operation our thread must do:

  1. Adding tasks
  2. Schedule operation (thanks to the co_await operator)
  3. A background infinite loop that will pop tasks and execute them.

There is nothing complicated, the run methods just wait for an element, pop awaiters, execute them if they are valid and that’s all.

The co_await operator will just push the coroutine_handle to the thread thanks to the Awaitable object.

Using this thread

We schedule the operations thanks to the co_await operator.
Here is an example, the task is a basic promise that never suspends. It means that the coroutine frame is destroyed at the end of the function.

The operation behind the first co_await runs on the first thread, the operation behind the second co_await runs on the second thread. Really simple.

Conclusion

We finished the first article about creating a thread pool using coroutines. We introduced some utility classes and designed a concurrent queue. If you want to try, you can find a full code here.

Thanks to  Nir Friedman to help me design mutex and condition variable in a better way :).