IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Cette utilisation de mutable est-elle raisonnable ?


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Par défaut Cette utilisation de mutable est-elle raisonnable ?
    Bonjour à toutes et à tous !

    J'utilise une librairie ayant une API C++ qui je crois est un peu vieillissante et ressemble à du C. J'ai récupéré un projet github proposant un début de wrapper d'une classe A en C++ moderne, auquel je rajoute les fonctionnalités dont j'ai besoin. Ce wrapper est grosso modo une classe B possédant un pointeur sur A.

    Mais je suis confronté (entre autres choses) à un problème de constance, parce qu'aucune méthode de A n'a été déclarée const.
    Est-ce une raison valable de déclarer le pointeur sur A comme mutable ?

  2. #2
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Bonjour,

    Citation Envoyé par Seabirds Voir le message
    Mais je suis confronté (entre autres choses) à un problème de constance, parce qu'aucune méthode de A n'a été déclarée const.
    Est-ce une raison valable de déclarer le pointeur sur A comme mutable ?
    Je crois que tu t'es emmêlés les pinceaux quelque part dans ton message.
    Un pointeur mutable, c'est un pointeur qu'on peut modifier pour qu'il pointe ailleurs. Un pointeur constant, c'est un pointeur qui pointe toujours au même endroit. Il n'y a pas de rapport avec la constness des méthodes du type pointé (A). Dans ta classe B, si tu déclares ton pointeur vers A avec le mot-clé mutable, alors ton pointeur sera mutable (c.-à-d. pourra être modifié pour pointer ailleurs) même dans les fonctions membres const de la classe B.
    Pourrais-tu écrire un petit bout de code ?

  3. #3
    Membre éclairé Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Par défaut
    Salut Pyramidev ! Merci de ta réponse !

    Oups oui je me suis bien emmêlé, sorry for that !

    En gros la libraire GDAL fournit la classe GDALDataset, qui contient des GDALRasterBand.

    La classe GDALDataset fournit cette méthode de lecture/écriture :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    CPLErr GDALDataset::RasterIO 	( 	GDALRWFlag  	eRWFlag,
    		int  	nXOff,
    		int  	nYOff,
    		int  	nXSize,
    		int  	nYSize,
    		void *  	pData,
    		int  	nBufXSize,
    		int  	nBufYSize,
    		GDALDataType  	eBufType,
    		int  	nBandCount,
    		int *  	panBandMap,
    		GSpacing  	nPixelSpace,
    		GSpacing  	nLineSpace,
    		GSpacing  	nBandSpace,
    		GDALRasterIOExtraArg *  	psExtraArg 
    	)
    Le code de wrapper que j'ai récupéré propose la classe Dataset avec quelques fonctionnalités, et je voudrais lui ajouter l'équivalent de la méthode RasterIO mais pour la lecture seule:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Dataset{
      std::unique_ptr<GDALDataset, custom_deleter> m_dataset;
      // ... features
     
      // je cherche à ajouter un truc du genre:
      return_type read( ... ) const {
     
        // ... mais ici on appelle une méthode non const sur la donnée membre.
        m_dataset->RasterIO(...);
     
      // retourner le résultat
      }
    };
    Du coup je voulais déclarer mutable la donnée membre m_dataset, mais mon inexpérience des pointeurs constants VS pointeurs sur donnée constante a parlé d'elle même x)
    Du coup je relis des trucs sur la constance et les pointeurs, mais quelle serait ici la meilleure manière de procéder ?
    [Edit] Minute papillon, si la constance du unique_ptr n'a rien à voir avec la constance de l'objet pointé, je peux directement appeler des méthodes non const sur la donnée pointée depuis la méthode read ?

    Question subsidiaires
    • il y a t'il une bonne raison de faire une seule méthode pour la lecture/écriture ?
    • la manière "moderne" de gérer le buffer, c'est bien le std::vector n'est ce pas ?

  4. #4
    Membre éclairé Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Par défaut
    Bon bah en fait ça marche (pas exactement comme présenté ci-dessus, mais dans l'idée oui).

    C'est bizarre, je m'étais complètement fait leurré par l'idée que "manipuler un pointeur unique sur un objet revient à manipuler l'objet lui-même" que j'avais étendu ça à unique_ptr constant = objet constant.
    Mouais bon clairement j'ai encore des lacunes de bases

    Merci encore pour ta réponse Pyramidev, mais je suis interessé aussi par les questions subsidiaires donc je ne vais pas de suite clore le sujet

  5. #5
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Ah, il s'agit donc d'un std::unique_ptr. Du coup, je comprends ta confusion parce que, pour moi aussi, il aurait été plus logique que std::unique_ptr propage const à la donnée pointée. En fin octobre 2016, j'avais ouvert un fil pour débattre sur ce sujet. C'était un débat intéressant.

    Citation Envoyé par Seabirds Voir le message
    il y a t'il une bonne raison de faire une seule méthode pour la lecture/écriture ?
    Le plus souvent, non. Pour écrire un code lisible et réutilisable, on essaie d'appliquer la règle "chaque fonction ne fait qu'une seule chose" et on nomme la fonction de manière à ce que ce nom décrive ce que fait la fonction.
    Par exemple, si on a une fonction size_t faireLaVaisselleEtCompterLeNombreDAssiettes(), il vaut mieux la décomposer en deux sous-fonctions : void faireLaVaisselle et size_t getNombreDAssiettes() const. Ce sera plus lisible et cela permettra au futur code d'appeler l'une sans appeler l'autre.
    L'exception à cette règle, c'est l'optimisation. Par exemple, std::pair<iterator,bool> std::set::insert(const value_type&) permet à la fois de vérifier si un élément existe déjà, de retourner l'adresse de cet élément s'il existe déjà, d'insérer un nouvel élément s'il n'existe pas déjà et de retourner l'adresse du nouvel élément s'il n'existe pas déjà. Cette fonction cumule donc beaucoup de responsabilités, mais permet de tout faire d'un coup en ne parcourant l'arbre qu'une seule fois.

    Citation Envoyé par Seabirds Voir le message
    la manière "moderne" de gérer le buffer, c'est bien le std::vector n'est ce pas ?
    De mon point de vue, si les performances sont importantes, pas forcément.
    Si on a un paramètre de type const std::vector<Type>& mais que l'utilisateur possède un tableau de type std::array<Type, Size>, alors cela oblige l'utilisateur à faire une conversion.
    Par contre, avoir un paramètre qui pointe vers le premier élément suivi d'un deuxième paramètre qui indique la taille permet d'éviter ce problème.

    Cela dit, une amélioration possible, ce serait de regrouper ces deux paramètres en un seul objet qui, en interne, contient un pointeur vers le premier élément et la taille. L'objet aurait des opérations pour accéder aux éléments, éventuellement avec des vérifications sur les indices. C'est le principe de gsl::span décrit dans les C++ Core Guidelines. Mais, actuellement, il n'y a pas de telle classe dans la bibliothèque standard du C++.

    A part ça, dans le cas de la programmation par template, la solution classique est d'avoir deux paramètres : un itérateur de début et un itérateur de fin, comme dans les fonctions de <algorithm>.
    Une version plus évoluée serait de regrouper ces deux itérateurs en un seul objet, un range. Mais, actuellement, les fonctions de <algorithm> ne peuvent pas encore avoir un range directement en argument : on est obligé de passer par la paire d'arguments début-fin.
    Cependant, n'oublions pas que la programmation par template complexifie le code. En plus, elle ralentit la compilation car, en général, tout le code se retrouve dans le ".h".

  6. #6
    Membre éclairé Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    Ah, il s'agit donc d'un std::unique_ptr. Du coup, je comprends ta confusion parce que, pour moi aussi, il aurait été plus logique que std::unique_ptr propage const à la donnée pointée.
    Woooh bah je me sens soulagé, j'avais l'atroce sensation d'être complètement à l'ouest ! Merci pour le lien, c'est vrai qu'elle est intéressante cette discussion !

    Le plus souvent, non. Pour écrire un code lisible et réutilisable, on essaie d'appliquer la règle "chaque fonction ne fait qu'une seule chose" et on nomme la fonction de manière à ce que ce nom décrive ce que fait la fonction.
    Ok, c'est bien ce qui me semblait, mais je me demandais si l'IO pouvait être une exception au SRP.

    A part ça, dans le cas de la programmation par template, la solution classique est d'avoir deux paramètres : un itérateur de début et un itérateur de fin, comme dans les fonctions de <algorithm>.
    Oui j'ai fini par capter ce truc, du coup c'est ce que j'utilise dans mes algos template

    Une version plus évoluée serait de regrouper ces deux itérateurs en un seul objet, un range. Mais, actuellement, les fonction de <algorithm> ne peuvent pas encore avoir un range directement en argument : on est obligé de passer par la paire d'arguments début-fin.
    Oh ! Donc ça a l'air d'une approche en train de devenir standard. J'avais commencé par mettre un objet range comme vu dans un tuto, mais j'avais retropédalé quand j'avais vu que le STL ne faisait pas comme ça.

    Cependant, n'oublions pas que la programmation par template complexifie le code. En plus, elle ralentit la compilation car, en général, tout le code se retrouve dans le ".h".
    Dieu merci je mets environ deux secondes à compiler, mais des heures à calculer. Ca veut dire que je peux encore mettre des trucs sur le dos du compilo ?

    Et un grand merci pour le lien vers les CPP core guide, je ne savais plus trop quoi lire !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. L'utilisation de CDN est-elle efficace
    Par bruce-willis dans le forum Performance Web
    Réponses: 3
    Dernier message: 22/07/2012, 19h26
  2. [Express Edition (gratuit)] Cette version est elle suffisante ?
    Par dymezac dans le forum MS SQL Server
    Réponses: 6
    Dernier message: 26/09/2006, 16h36
  3. cette requête est-elle correcte?
    Par spilliaert dans le forum Requêtes
    Réponses: 1
    Dernier message: 02/02/2006, 22h33
  4. Cette requête est-elle valide?
    Par FrankOVD dans le forum Requêtes
    Réponses: 4
    Dernier message: 13/01/2006, 19h21
  5. Utilisation de composant sans Form est elle possible
    Par Hypollite76 dans le forum Composants VCL
    Réponses: 26
    Dernier message: 01/12/2005, 12h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo