NotANameServer / discord

Règles et gestion du serveur Discord de programmation Not a Name : https://discord.gg/zcWp9sC
23 stars 9 forks source link

C++ TL;DR news 2 #26

Closed GuillaumeBelz closed 3 years ago

GuillaumeBelz commented 3 years ago

Cet revue d'articles sera publiée le dimanche 23 mai. Postez ici vos propositions de revue.

Vous pouvez vous inspirer du premier billet pour rédiger votre revue : https://zestedesavoir.com/billets/3930/c-tl-dr-news-1/. Essayer d'etre clair, concis.

GuillaumeBelz commented 3 years ago

quelques lectures prochaines possible :

A noter, ce n'est pas nécessairement des articles publiés cette semaine, mais plus mes lectures de la semaine. Si vous lisez un article publié il y a 2 ans et qu'il est intéressant, ça va aussi,

LucHermitte commented 3 years ago

J'ai bien aimé ce billet chez redhat il y a quelques semaines https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind (il y en a eu d'autres de sympas peu après).

GuillaumeBelz commented 3 years ago

Je pourrais mettre aussi dans cette revue les relectures de livres, comme celle que j'ai fait (rapidement) pour Charles : https://guillaumebelz.github.io/critiques.html. C'est une critique basé sur un survol du PDF, mais c'est probablement ok de publier ca.

robin850 commented 3 years ago

Très sympa le concept du billet.

Quelques propositions :

GuillaumeBelz commented 3 years ago
GuillaumeBelz commented 3 years ago

A Default Value to Dereference Null Pointers

Lire l'article original : https://www.fluentcpp.com/2021/05/14/a-default-value-to-dereference-null-pointers/

std::optional = nullable object, avec une bonne sémantique pour gérer le cas ou c'est null. optional peut avoir toutes les valeurs de T + nullopt (= not set) sans avoir besoin d'utiliser une valeur spécifique

std::optional<int> f()
{
    if (thereIsAnError) return std::nullopt;

    // happy path now, that returns an int
}

auto result = f();
std::cout << (result ? *result : 42) << '\n';

std::cout << f().value_or(42) << '\n';

Pour les pointeurs, nullable aussi, mais la dernière syntaxe n'est pas utilisable. Comment ajouter value_or pour les pointeurs ?

template<typename T, typename U>
decltype(auto) value_or(T* pointer, U&& defaultValue)
{
    return pointer ? *pointer : std::forward<U>(defaultValue);
}

Le type de retour (rvalue ou lvalue) va dépendre de la catégorie de valeur de la valeur par défaut.

GuillaumeBelz commented 3 years ago
GuillaumeBelz commented 3 years ago

C++20 Coroutine: Under The Hood

Lire l'article d'origine : http://www.vishalchovatiya.com/cpp20-coroutine-under-the-hood/

Suite d'un article pour faire des "coroutine" en C, avec les fonctions systèmes. Le lien dans l'article. Quelques notions sont présentées dans ce premier article.

Coroutine : une fonction qui peut être suspendu et reprise. Peut être vu comme intermédiaire entre fonction et thread. Comparé aux threads :

resume

En pratique, qu'est-ce qu'une coroutine en C++20 ? Une fonction qui contient co_await, co_yield et/ou co_return, et peu retourner std::promise.

Du point de vue haut niveau, une coroutine est :

L'article donne des liens vers 2 exemples d'utilisation de coroutines, dans le design pattern Iterator et dans un générateur de séquence d'entiers.

Suspendre une coroutine

struct HelloWorldCoro {
    struct promise_type { // compiler looks for `promise_type`
        HelloWorldCoro get_return_object() { return this; }    
        std::suspend_always initial_suspend() { return {}; }        
        std::suspend_always final_suspend() { return {}; }
    };
    HelloWorldCoro(promise_type* p) : m_handle(std::coroutine_handle<promise_type>::from_promise(*p)) {}
    ~HelloWorldCoro() { m_handle.destroy(); }
    std::coroutine_handle<promise_type>      m_handle;
};

Dans ce code :

Utilisation :

HelloWorldCoro print_hello_world() { // Iigne 1
    std::cout << "Hello "; // ligne 2
    co_await std::suspend_always{}; // ligne 3
    std::cout << "World!" << std::endl; // ligne 4
} // ligne 5

int main() {
    HelloWorldCoro mycoro = print_hello_world(); // ligne A
    mycoro.m_handle.resume(); // ligne B
    mycoro.m_handle.resume(); // ligne C
    return EXIT_SUCCESS; // ligne D
}

Pour résumer ce qu'il se passe, le premier resume affiche Hello, le second affiche World!. Pour entrer plus dans les détails, le flux d'exécution suit les étapes suivantes :

L'article entre plus en détail sur les codes intermédiaires qui sont générés lors de la compilation.

Note : l'exécution de la coroutine est suspendue juste après le lancement de celle-ci et la ligne 2 n'est pas appelée avant le premier resume. Cela est dû au fait que la fonction initial_suspend retourne std::suspend_always. Il est possible de changer ce comportement, pour que la ligne 2 soit exécutée dès l'appel à la coroutine, en utilisant le type standard std::suspend_never.

Retourner une valeur depuis une coroutine

Comme une coroutine doit retourner le "promise" pour contrôler le flux d'exécution, il n'est pas possible de retourner directement une valeur, comme pour une fonction normale. Pour cela, il faut co_return et return_value :

struct HelloWorldCoro {
    struct promise_type {
        int m_value;
        void return_value(int val) { m_value = val; }
        ...
    };
};

HelloWorldCoro print_hello_world() {
    std::cout << "Hello ";
    co_await std::suspend_always{ };
    std::cout << "World!" << std::endl;
    co_return -1;
}

int main() {
    HelloWorldCoro mycoro = print_hello_world();
    mycoro.m_handle.resume();
    mycoro.m_handle.resume();
    assert(mycoro.m_handle.promise().m_value == -1);
    return EXIT_SUCCESS;
}

Dans ce code, la valeur de retour est déclarée dans la structure promise_type, avec la fonction return_value. Dans la coroutine, une valeur est retournée par co_return, puis cette valeur est récupérée via le promise mycoro.m_handle.promise().m_value.

Rendre une valeur de Coroutine

La valeur retournée par la coroutine ne peut être modifiée qu'une seule fois, lors de l'appel à co_return, ce qui stop l'exécution de la coroutine. Si la ligne contenant co_return dans le code précédent est déplacée après la ligne suspend_always, le texte "World!" ne sera jamais affiché.

Mais dans un code asynchrone comme celui-ci, il peut être intéressant de retourner une valeur à chaque fois que la coroutine est suspendue. Pour cela, il faut utiliser co_yield et yield_value :

struct HelloWorldCoro {
    struct promise_type {
        int m_val;
        std::suspend_always yield_value(int val) {
            m_val = val; 
            return {};
        }
        ...
    };
};

HelloWorldCoro print_hello_world() {
    std::cout << "Hello ";
    co_yield 1;
    std::cout << "World!" << std::endl;
}

int main() {
    HelloWorldCoro mycoro = print_hello_world();
    mycoro.m_handle.resume();
    assert(mycoro.m_handle.promise().m_val == 1);
    mycoro.m_handle.resume();
    return EXIT_SUCCESS;
}

Contrairement à co_return qui stoppait l'exécution de la coroutine, co_yield suspend simplement la coroutine en retournant une valeur. La coroutine peut être reprise ensuite.

Pour résumer :

Terminologie utilisée avec les coroutine en C++20

Les opérateurs unaires :

L'article contient plus de détails sur le fonctionnement interne et l'implémentation possible des coroutines en C++20. Si vous voulez entrer dans les profondeurs des coroutines, vous pouvez lire la série d'articles de Raymond Chen : https://devblogs.microsoft.com/oldnewthing/20210504-01/?p=105178

GuillaumeBelz commented 3 years ago

CPPFrUG 45

Le C++ French User Group organise régulièrement des soirées, avec des présentations et discussions sur le C++. Ce groupe se réunit normalement sur Paris, mais depuis le covid, les soirées sont organisées en ligne.

La prochaine demain est demain (mardi 25 mai). Pour s'inscrire : https://www.meetup.com/fr-FR/User-Group-Cpp-Francophone/events/278281513/

GuillaumeBelz commented 3 years ago

Function Templates - More Details about Explicit Template Arguments and Concepts

Lire l'article d'origine : http://www.modernescpp.com/index.php/function-templates-more-details

Cet article présente deux nouvelles "règles" de bonne pratique, sur les arguments template (C++17) et les concepts (C++20).

Explicitly Specifying the Template Arguments

Cette "règle" est très simple :

std::vector<int> myVec{1, 2, 3, 4, 5};  // avant C++17
std::vector myVec{1, 2, 3, 4, 5};       // depuis le C++17

Il faut préférer la seconde syntaxe au lieu de la première.

Cette règle peut surprendre, mais elle est en fait logique : elle est l'équivalent de la même règle pour les fonctions, appliquée aux classes. Pour une fonction template, on va généralement préférer la déduction des arguments template selon les arguments de la fonction :

template <typename T>
T max(const T& lhs,const T& rhs);

auto res1 = max<float>(5.5, 6.0);   // non
auto res2 = max(5.5, 6.0);          // oui

Dans le cas d'un overload de fonctions template et non template :

double max(const double& lhs, const double& rhs);

template <typename T>
T max(const T& lhs,const T& rhs);

auto res1 = max(5.5, 6.0);    // (1)
auto res2 = max<>(5.5, 6.0);  // (2)

Ce code n'est pas ambigüe, du fait que la ligne (1), qui peut utiliser les 2 fonctions, va préférer la fonction non template et la ligne (2) ne va considérer r que la fonction template.

Overloading with Concepts

La seconde "règle" consiste simplement à contraindre par défaut les paramètres template en utilisant les concepts.

MyClass max(MyClass lhs, MyClass rhs);

template <std::totally_ordered T>
T max(const T& lhs,const T& rhs)

template <typename T>
T max(const T& lhs,const T& rhs);

auto value2 = max(MyClass{1}, MyClass{2});   // (1)
auto value2 = max(1, 2);                     // (2)

Dans ce code, la ligne (1) n'est pas ambigue, puisqu'elle va préférer la fonction non template. La ligne (2) n'est pas non plus ambigue, puisqu'elle va préférer la fonction template avec contrainte (par le concept std::totally_ordered).

GuillaumeBelz commented 3 years ago

Publié https://zestedesavoir.com/billets/3944/c-tl-dr-news-2/