Chaque jour de décembre a droit à sa surprise. Après l'ordre d'évaluation, aujourd'hui, le calendrier de l’Avent du C++ présente la Spécification Technique P0061 concernant une macro magique : #define __has_include
.
Sommaire
- Série de dépêches C++
- Spécification Technique
- La macro
- Dépendance optionnelle
- Roue de secours
- Faut‐il continuer à apprendre le C++ ?
- Réutilisation
- Les auteurs
- Continuer à améliorer ce document
- La suite
Série de dépêches C++
Cette dépêche fait partie de toute une série disponible également surle dépôt Git du Groupe des utilisateurs C++ francophone.
Publication | Dépêche |
---|---|
20 août 2016 | Les coulisses du standard C++ |
2 oct. 2016 | Genèse du C++17 |
1ᵉʳ déc. 2016 | C++17 fixe l’ordre d’évaluation des expressions |
2 déc. 2016 | C++17 indique la disponibilité des entêtes (header) |
à venir… | … d’autres dépêches … |
en 2017 | Faut‐il continuer à apprendre le C++ ? |
Initialement, nous allions publier une grosse dépêche super trop longue. Puis, fin novembre, nous nous sommes ravisés et nous vous proposons plutôt une petite dépêche par jour, d’où l’idée du calendrier de l’Avent du C++. (ღˇ◡ˇ)~♥
Spécification Technique
La macro __has_include
est dans les cartons depuis plusieurs années. Le comité de normalisation du C++ a intégré cette fonctionnalité en amendant le TS P0061.
La macro
La macro __has_include()
vérifie si l’en‐tête est disponible pour inclusion.
#if __has_include(<filesystem>)
# include <filesystem>
#elif __has_include(<experimental/filesystem>)
# include <experimental/filesystem>
#elif __has_include(<boost/filesystem.hpp>)
# include <boost/filesystem.hpp>
#else
# error Ne trouve aucune en-tête filesystem
#endif
Dépendance optionnelle
Sans cette fonctionnalité, le code source avait moins de possibilités de s’adapter automatiquement à l’environnement de compilation. Pour les dépendances optionnelles, l’outil de compilation (autotools, CMake…) devaient détecter la présence de telle ou telle dépendance et passer au compilateur des macros pour activer ou désactiver des parties du code source.
Et sans cette complexité en amont, il est difficile de proposer du code C ou C++ qui gère des dépendances optionnelles : si l’en‐tête (header) d’une dépendance est absent, le compilateur arrête la compilation, car le code source tente d’inclure l’en‐tête introuvable de cette dépendance, même si la dépendance est optionnelle.
Chère lectrice, cher lecteur LinuxFr.org, tu as peut‐être un exemple pertinent en tête.
Tu souhaites déplacer la complexité de l’outil de compilation vers le code source ?
Fais‐nous part de tes idées dans les commentaires.
L’exemple ci‐dessous illustre l’utilisation de la macro __has_include()
, mais aurait aussi pu se baser sur la détection de macros comme WIN32
, _WIN64
ou MSCVER
:
#if __has_include(<windows.h>)
# include <windows.h>
LONGLONG ticks1nano = []() {
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
return freq.QuadPart / 1000'000;
}();
LONGLONG nanosecondes() {
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
return time.QuadPart/ticks1nano;
}
#elif __has_include(<time.h>)
# include <time.h>
auto nanosecondes() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC,&ts);
return 1000'000'000 * ts.tv_sec + ts.tv_nsec;
}
#else
# error Ne trouve ni <windows.h> ni <time.h>
#endif
L’exemple suivant utilise également la macro __has_include()
.
C’est une possibilité pour une implémentation multi‐plate‐forme du futur Networking TS.
#if __has_include(<winsock2.h>)
#include <winsock2.h>
struct WindowsSocketImpl : AbstractSocket
{
// implémentation Windows
};
using MySocket = WindowsSocketImpl;
#else
#include <sys/socket.h>
struct UnixSocketImpl : AbstractSocket
{
// implémentation Unix
};
using MySocket = UnixSocketImpl;
#endif
// Usage
AbstractSocket * socket = new MySocket();
Roue de secours
Nous pouvons aussi imaginer l’utilisation de cette macro __has_include()
pour sélectionner la bibliothèque à utiliser selon la disponibilité de différentes alternatives :
#if __has_include(<optional>)
#include <optional>
using MyOptional = std::optional;
#elif __has_include(<experimental/optional>)
#warning Utilise std::experimental::optional à la place de std::optional
#include <experimental/optional> // roue de secours
using MyOptional = std::experimental::optional;
#elif __has_include(<boost/optional.hpp>)
#warning Utilise boost::optional à la place de std::optional
#include <boost/optional.hpp> // roue de secours secondaire
using MyOptional = boost::optional;
#else
# error Ne trouve ni <optional>, ni <experimental/optional>, ni <boost/optional>
#endif
Faut‐il continuer à apprendre le C++ ?
Ne pas nourrir les trolls | Ne pas nourrir les trolls |
Merci de nous aider à structurer et consolider les différentes idées sur cette question dans l’espace de rédaction collaboratif de LinuxFr.org : Faut‐il continuer à apprendre le C++ ?
Réutilisation
Le texte de cette dépêche est protégé par le droit d’auteur la gauche d’auteur et réutilisable sous licence CC BY-SA 4.0. Les images utilisées sont aussi sous licence libre (cliquer sur l’image pour plus de détails).
Donc, n’hésitez pas à réutiliser ce contenu libre pour créer, par exemple, des supports de formation, des présentations (Meetups), des publications sur d’autres blogs, des articles pour des magazines, et aussi un article C++17 sur Wikipédia dès que Wikipédia passera de la licence CC BY-SA 3.0 à la CC BY-SA 4.0 (le contenu de cette dépêche utilise la version la CC BY-SA 4.0).
Les auteurs
Par respect de la licence, merci de créditer les auteurs :
- les principaux auteurs sont Adrien Jeser et Oliver H. ;
- les nombreux autres contributeurs ayant contribué sur l’ancêtre de cette dépêche ou sur le dépôt Git sont : eggman, Yves Bourguignon, Storm, gorbal, palm123, khivapia, BAud, Segfault, Benoît Sibaud, Lucas, cracky, Martin Peres, RyDroid, olibre et Guss.
Continuer à améliorer ce document
Malgré tout le soin apporté, il reste certainement des oublis, des ambiguïtés, des fôtes… Bien que cette dépêche restera figée sur le site LinuxFr.org, il est possible de continuer à l’enrichir sur le dépôt Git du Groupe des utilisateurs C++ francophone (C++FRUG). C’est donc sur ce dépôt que se trouvent les versions les plus à jour. (ღ˘⌣˘ღ)
Alors que cet article restera figé sur le site LinuxFr.org, il continuera d’évoluer sur le dépôt Git. Merci de nous aider à maintenir ce document à jour avec vos questions/suggestions/corrections. L’idée est de partager ce contenu libre et de créer/enrichir des articles Wikipédia quand la licence sera CC BY-SA 4.0. ٩(•‿•)۶
La suite
La dépêche suivante nous dévoilera une autre nouveauté du C++17.
Chère lectrice, cher lecteur LinuxFr.org. Tu souhaites apporter ta pierre à cet édifice ? Rejoins‐nous dans l’espace de rédaction collaborative sur LinuxFr.org (un compte est nécessaire pour y accéder).
À suivre…
Aller plus loin
- Dépêche préliminaire dans « Les coulisses du standard C++ » (CC BY-SA 4.0) (511 clics)
- Dépêche de mise en bouche racontant la genèse du C++17 (CC BY-SA 4.0) (423 clics)
- Dépêche du 1ᵉʳ décembre « C++17 fixe l’ordre d’évaluation » (CC BY-SA 4.0) (145 clics)
- Contenu markdown de cette dépêche sur le dépôt Git du C++FRUG (113 clics)
- Spécification Technique P0061 « `__has_include` for C++17 » (124 clics)
# Coquille
Posté par derderder . Évalué à 2.
s/difficil/difficile/g
[^] # Re: Coquille
Posté par lmg HS (site web personnel) . Évalué à 2. Dernière modification le 02 décembre 2016 à 10:16.
s/des macro/&s/
s/pertienent/pertinent/
[^] # Re: Coquille
Posté par Benoît Sibaud (site web personnel) . Évalué à 3.
Corrigé, merci.
[^] # Re: Coquille
Posté par windu.2b . Évalué à 2.
s/optionelle/optionnelle/g
[^] # Re: Coquille
Posté par Benoît Sibaud (site web personnel) . Évalué à 3.
Corrigé, merci.
# Double underscore
Posté par David Demelier (site web personnel) . Évalué à 5.
J'avais vu cette fonctionnalité il y a longtemps, mais je ne comprends toujours pas pourquoi elle a été intégrée avec un double underscore.
Toutes les instructions du préprocesseurs n'en comportaient pas :
#define
#if
#else
#endif
#elif
#pragma
#undef
#__has_include // WTF??
git is great because linus did it, mercurial is better because he didn't
[^] # Re: Double underscore
Posté par Zenitram (site web personnel) . Évalué à 5. Dernière modification le 02 décembre 2016 à 11:47.
surtout que "__" est sensé être reservé à unsage du compilo ou de l'utilisateur, bref le standard "promettait" de ne pas y toucher, si j'ai bien suivi :
http://www.doc.ic.ac.uk/lab/cplus/c++.rules/chap5.html
"The use of two underscores (`__') in identifiers is reserved for the compiler's internal use according to the ANSI-C standard. "
A priori c'est ça dans la spec (que je n'ai pas) :
Bref, c'est étonnant en effet…
toutefois, ta comparaison n'est pas bonne :
Ce n'est pas "#__has_include" mais "#if __has_include", donc ça n'a pas grand chose à voir avec les '#'
(__has_include est une macro)
[^] # Re: Double underscore
Posté par David Demelier (site web personnel) . Évalué à 1.
Aaaah oui bien vu ! Du coup tout s'explique :)
git is great because linus did it, mercurial is better because he didn't
[^] # Re: Double underscore
Posté par Oliver (site web personnel) . Évalué à 4. Dernière modification le 02 décembre 2016 à 11:53.
(Oups, je n'avais pas vu que Zenitram avait déjà répondu pendant que je rédigeais cette réponse)
__has_include
ne s'utilise pas avec un croisillon (carré) comme#__has_include
mais s'utilise comme toutes les macros du préprocesseur définies avec#define
. Par exemple :Le préfixe avec le double tiret bas (underscore) permet d'indiquer que c'est un identifiant réservé (ne pas utiliser d'identifiant dans votre code source qui pourrait contenir deux tirets bas successifs
__
quelque soit sa position, au début, au milieu ou à la fin).__has_include
n'est pas un mot-clé du C++, mais une extension du compilateur qui est en cours de standardisation pour C++17.Commentaire sous licence Creative Commons Zero CC0 1.0 Universal (Public Domain Dedication)
[^] # Re: Double underscore
Posté par jcelerier . Évalué à 2.
En faisant une petite recherche Github sur "has_include" :
https://github.com/search?l=C%2B%2B&p=2&q=has_include&type=Code&utf8=%E2%9C%93
On voit qu'il y a déjà du code qui s'en sert comme un identifiant. Vu que c'est nécessairement une macro de préprocesseur, avoir #define has_include casserait le code existant, ce que le standard essaye d'éviter. S'ils avaient eu cette idée il y a 30 ans on n'en serait peut-être pas là :p
# L'intérêt ???
Posté par mpurple . Évalué à 5.
Je ne vois franchement pas l'intérêt de cet apport.
Pour contourner le problème de Windows ou Unix, il y a déjà la macro WIN32.
Pour le reste on est le plus souvent obligé d'indiquer des chemins pour les fichiers d'en-tête.
ou
Et si c'est pour utiliser des API différentes, alors mieux vaut avoir des fichiers sources différents, ce qui se gère au niveau du Makefile.
Et j'y vois de nouveaux inconvénients:
- des nouveaux blocs de codes morts, au lieu de fichiers de code par cible.
- des mélanges d'utilisation avec les macros déjà existantes, donc de nouvelles illisibilités dans le code.
J'aurais préféré un système pour connaître la version d'une API. Là il y aurait un vrai plus et on aurait pu faire du ménage dans les préprocesseurs de Makefile. (adieu autotool et consort).
Je ne trouve pas les exemples proposés convaincants.
Je suis prêt à changer d'avis si vous m'en proposez d'autres.
[^] # Re: L'intérêt ???
Posté par lmg HS (site web personnel) . Évalué à 4.
Sur des pet projects mono-fichier et sans Makefile, cela permet d'inclure et utiliser simplement
std::string_view
qui sera dans expérimental ou pas p.ex. C'est sûr que ce n'est pas terrible, mais c'est un moyen de s'en sortir uniquement avec le préprocesseur.[^] # Re: L'intérêt ???
Posté par mpurple . Évalué à 0.
Merci, mais on ne peut pas parler de projet avec un seul fichier, et même là, la ligne de compilation suffit.
[^] # Re: L'intérêt ???
Posté par lmg HS (site web personnel) . Évalué à 0.
Injecter
-Iunchemin
dans la ligne de compilation c'est une chose, injecter en plus un-Dtrucquisededuitdufichiervisibledans$INCLUDE
, ce n'est pas DRY: on répète des informations que l'outil aurait pu déduire pour nous.De plus, quid des projets sur gcc.goldbot. Comment savoir à l'avance si on va disposer d'une fonctionnalité ou d'autre autre?
# Cas "optional"
Posté par jcelerier . Évalué à 2.
Je m'en sers quasiment exactement comme ça dans mon code : https://github.com/OSSIA/i-score/blob/master/base/lib/iscore/tools/std/Optional.hpp
Mais le optional boost et le optional std n'ont pas une API compatible, par exemple pour vider un optional c'est
boost::none
vsstd::nullopt
donc il faut définir des choses en plus :( :( :( :([^] # Re: Cas "optional"
Posté par tyoup . Évalué à -1.
boire ou conduire, il faut choisir
# Et le choix de compilation de l'utilisateur ?
Posté par nado . Évalué à 1.
Si c’est géré au niveau du préprocesseur, comment peut-on empêcher l’utilisation de ces codes si on n’en veut pas ? Par exemple si on a deux implémentations d’une lib installées et qu’on souhaite que le programme à compiler en utilise une au lieu de l’autre, c’est possible de le gérer ?
[^] # Re: Et le choix de compilation de l'utilisateur ?
Posté par lmg HS (site web personnel) . Évalué à 1.
Pour moi ça se fait en amont dans la chaine de compilation où l'on va forcer quelle source d'une bibliothèque on veut utiliser.
__has_include
ne me parait pas du tout adapté. Les inline namespace à la limite, mais faut-il encore que la bibliothèque les utilise, et ça ce n'est pas gagné.[^] # Re: Et le choix de compilation de l'utilisateur ?
Posté par Oliver (site web personnel) . Évalué à 1. Dernière modification le 05 décembre 2016 à 23:54.
Si j'ai bien compris, tu veux compiler un logiciel qui utilises
__has_include(<header>)
et tu ne veux pas que ce code source choisisse le<header>
à inclure => tu veux forcer l'inclusion du<header>
de ton choix et non pas que ce soit le code source qui gère cette décision.Oui c'est possible, tu définis les chemins d'inclusion que tu souhaites. Par exemple, avec GCC et Clang, tu peux utiliser les options
-I
et-isystem
(d'autres options sont disponibles pour contrôler l'inclusion des entêtes).Est-ce que cela répond à ta question ?
Commentaire sous licence Creative Commons Zero CC0 1.0 Universal (Public Domain Dedication)
# Typo ×2
Posté par djano . Évalué à 3.
roue de secour => roue de secours
[^] # Re: Typo ×2
Posté par Benoît Sibaud (site web personnel) . Évalué à 3.
Corrigé, merci.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.