C++ 4 min de lecture

Comment les développeurs C++ avancés écrivaient du code moderne avant l’avènement des normes modernes

Share this article
Comment les développeurs C++ avancés écrivaient du code moderne avant l’avènement des normes modernes

Avant l’avènement de C++11 et des normes suivantes, les développeurs C++ avancés devaient trouver des moyens ingénieux d’écrire du code moderne, maintenable et efficace. Voici quelques exemples de la façon dont ils implémentaient des fonctionnalités qui ont ensuite été normalisées :

1- shared_ptr

Les pointeurs intelligents étaient utilisés en C++ depuis longtemps, souvent via des implémentations personnalisées ou des bibliothèques comme Boost. Pour beaucoup de développeurs C++, l’introduction de shared_ptr et des autres pointeurs intelligents dans la bibliothèque standard n’a fait que normaliser des outils qu’ils utilisaient déjà.

#include <boost/shared_ptr.hpp>
boost::shared_ptr<int> p(new int(10));

2- La boucle for basée sur les plages

BOOST_FOREACH est une macro fournie par la bibliothèque Boost en C++ pour simplifier l’itération sur les conteneurs. Elle est antérieure à la boucle for basée sur les plages introduite en C++11 et offre une façon d’écrire du code plus propre et plus lisible.

#include <boost/foreach.hpp>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    BOOST_FOREACH(int& x, vec) {
        std::cout << x << " ";
    }
    return 0;
}

3- Les concepts C++

Avant les concepts, enable_if de <type_traits> servait à activer conditionnellement des templates de fonctions et des spécialisations de classes en fonction de traits. C’est plus verbeux et plus difficile à lire, ce qui conduit souvent à une syntaxe complexe.

template <typename T>
std::enable_if_t<std::is_integral_v<T>, T> add(T a, T b) {
    return a + b;
}

4- Les expressions lambda

La bibliothèque Boost Lambda permettait de créer de petits objets-fonctions anonymes (lambdas) en C++, utilisables en ligne là où un objet-fonction ou un foncteur est requis. C’est particulièrement utile pour les opérations courtes et cela rend le code plus concis et lisible.

#include <boost/lambda/lambda.hpp>
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    using namespace boost::lambda;
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::for_each(vec.begin(), vec.end(), std::cout << (_1 * 2) << " ");
    return 0;
}

Ce code affiche chaque élément du vecteur multiplié par 2.

5- constexpr

Avant que constexpr ne soit introduit en C++11, les développeurs s’appuyaient souvent sur Boost pour obtenir une évaluation constante similaire à la compilation. La bibliothèque Boost offre plusieurs utilitaires et techniques qui peuvent imiter le comportement de constexpr.

Bien que ce ne soit pas un remplacement direct, BOOST_STATIC_ASSERT peut servir à imposer des assertions à la compilation, garantissant que certaines conditions sont remplies pendant la compilation.

Exemple :

#include <boost/static_assert.hpp>

BOOST_STATIC_ASSERT(sizeof(int) == 4);

Boost.MPL fournit aussi des outils de calcul à la compilation, utilisables pour obtenir un comportement similaire à constexpr pour la métaprogrammation par templates.

Exemple :

#include <boost/mpl/int.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/static_assert.hpp>

namespace mpl = boost::mpl;

typedef mpl::int_<3> three;
typedef mpl::int_<4> four;
typedef mpl::plus<three, four>::type seven;

BOOST_STATIC_ASSERT((mpl::equal_to<seven, mpl::int_<7>>::value));

Enfin, Boost.Hana est une bibliothèque de métaprogrammation moderne qui offre des capacités à la compilation plus puissantes et flexibles, similaires à constexpr.

Exemple :

#include <boost/hana.hpp>
#include <iostream>

namespace hana = boost::hana;

constexpr auto sum = hana::int_c<3> + hana::int_c<4>;

int main() {
    std::cout << sum << std::endl; // Outputs: 7
    return 0;
}

6- Les traits de types

C++11: std::is_same, std::enable_if, etc.
Boost: boost::is_same, boost::enable_if, etc.

  • Boost fournissait des traits de types et des utilitaires SFINAE pour la métaprogrammation par templates.

Exemple :

#include <boost/type_traits.hpp>
boost::is_same<int, int>::value; // true

7- Les assertions statiques

C++11: static_assert
Boost: BOOST_STATIC_ASSERT

  • Boost permettait des assertions à la compilation pour détecter les erreurs au plus tôt.

Exemple :

#include <boost/static_assert.hpp>
BOOST_STATIC_ASSERT(sizeof(int) == 4);

8- Les expressions régulières

C++11: std::regex
Boost: boost::regex

  • Boost fournissait une puissante bibliothèque d’expressions régulières.

Exemple :

#include <boost/regex.hpp>
boost::regex re("^[0-9]+$");

9- Les tuples

C++11: std::tuple
Boost: boost::tuple

  • Boost offrait un moyen de retourner plusieurs valeurs depuis une fonction.

Exemple :

#include <boost/tuple/tuple.hpp>
boost::tuple<int, std::string> t(1, "example");

10- Chrono

C++11: std::chrono
Boost: boost::chrono

  • Boost fournissait des utilitaires pour les durées et les horloges.

Exemple :

#include <boost/chrono.hpp>
boost::chrono::steady_clock::time_point start = boost::chrono::steady_clock::now();

11- Les threads

C++11: std::thread
Boost: boost::thread

  • Boost fournissait des capacités de multithreading avant leur normalisation.

Exemple :

#include <boost/thread.hpp>
boost::thread t([]{ std::cout << "Threading with Boost"; });
t.join();

Voici quelques exemples supplémentaires de fonctionnalités que Boost fournissait avant leur normalisation en C++11 et versions ultérieures :

12- Les objets-fonctions

C++11: std::function
Boost: boost::function

  • Boost offrait boost::function pour stocker des objets appelables.

Exemple :

#include <boost/function.hpp>
boost::function<int(int, int)> f = std::plus<int>();

13- Bind

C++11: std::bind
Boost: boost::bind

  • Boost permettait de lier des arguments de fonction et de créer des applications partielles de fonctions.

Exemple :

#include <boost/bind.hpp>
auto bound_func = boost::bind(std::plus<int>(), 10, _1);
std::cout << bound_func(5); // Outputs 15

14- Optional

C++17: std::optional
Boost: boost::optional

  • Boost fournissait boost::optional pour les valeurs optionnelles, utile pour les fonctions susceptibles de ne pas retourner de valeur.

Exemple :

#include <boost/optional.hpp>
boost::optional<int> maybe_value;
maybe_value = 10;
if (maybe_value) {
    std::cout << *maybe_value;
}

15- Any

C++17: std::any
Boost: boost::any

  • Boost fournissait un conteneur à typage sûr pour des valeurs uniques de tout type.

Exemple :

#include <boost/any.hpp>
boost::any value = 10;
try {
    std::cout << boost::any_cast<int>(value);
} catch (boost::bad_any_cast &) {
    std::cout << "Bad cast!";
}

16- Variant

C++17: std::variant
Boost: boost::variant

  • Boost fournissait une union à typage sûr pouvant contenir l’un de plusieurs types.

Exemple :

#include <boost/variant.hpp>
boost::variant<int, std::string> var;
var = 10;
std::cout << boost::get<int>(var);

17- Filesystem

C++17: std::filesystem
Boost: boost::filesystem

  • Boost fournissait des fonctionnalités pour les opérations sur le système de fichiers.

Exemple :

#include <boost/filesystem.hpp>
boost::filesystem::path p("/path/to/file");
if (boost::filesystem::exists(p)) {
    std::cout << "File exists.";
}

Conclusion

Boost a historiquement comblé de nombreuses lacunes de la bibliothèque standard C++, en fournissant des fonctionnalités et utilitaires avancés qui ont ensuite été adoptés par la norme C++. Ces exemples montrent comment Boost a aidé les développeurs C++ à écrire du code plus expressif et plus efficace avant que ces fonctionnalités ne soient officiellement normalisées en C++11 et versions ultérieures.

Share this article