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 :

Classe Template et fonction virtuelle cachée


Sujet :

C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    7
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mars 2010
    Messages : 7
    Points : 12
    Points
    12
    Par défaut Classe Template et fonction virtuelle cachée
    Bonjour,

    J'ai codé une classe template permettant d'accéder à une table de base de données.

    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
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    template <class T>
    class IDbfIO
    {
    public:
    	/// \brief Ecrit un enregistrement (non impl)
    	virtual writeRecord( const T & data )
    	{
    	}
     
    	/// \brief Modifie un enregistrement (non impl)
    	virtual void editRecord( const T & data )
    	{
    	}
     
    	/// \brief Lit un enregistrement (non impl)
    	virtual void readCurrentRecord( T & data )
    	{
    	}
     
    	/// \brief Lit tous les enregistrements dans un vecteur
    	virtual void readAllRecords( vector<T> & aData )
    	{
    		int i = 0;
    		aData.resize( m_pTable->RecordCount );
     
    		m_pTable->First();
     
    		// lecture du fichier
    		while ( !m_pTable->Eof )
    		{
    			readCurrentRecord( aData.at( i ) );
    			i++;
    			m_pTable->Next();
    		}
    	}
     
    	/// \brief Lit tous les enregistrements dans une map
    	virtual void readAllRecords( map<AnsiString,T> & mapData )
    	{
    		AnsiString strKey = "";
    		T data;
     
    		m_pTable->First();
     
    		// lecture du fichier
    		while ( !m_pTable->Eof )
    		{
    			readCurrentRecord( strKey, data );
     
    			if( !mapData.count( strKey ) )
    			{
    				mapData[strKey] = data;
    			}
    			else
    			{
                                                 //log
    			}
     
    			m_pTable->Next();
    		}
    	}
    };
    Cette architecture a un double intérêt :
    - me permettre de factoriser du code ( par exemple : les fonctions lisant l'intégralité de la table ).
    - forcer les classes filles implémentant IDbfIO a surcharger des fonctions (par exemple : readCurrentRecord() ).

    Par exemple voici une classe fille :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class CDbfService : public IDbfIO<CDataService>
    {
    public:
    	CDbfService();
    	~CDbfService();
     
    	/// \brief Specialisation de lecture pour les CDataService
    	/// \param data : donnees du service
    	void readCurrentRecord( CDataService & data );
    };
    Elle supplante readCurrentRecord(). Jusque là pas de soucis, c'est lorsqu'elle supplante readAllRecords( vector<CDataService> & aData ) que j'ai un soucis.

    En effet, j'ai une warning me précisant que la fonction readAllRecords( map<AnsiString,T> & mapData ) est cachée.

    J'ai donc creusé le sujet et compris qu'il fallait que je rajoute la ligne

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using CDbfIO::readAllRecords( map<AnsiString,T> & mapData );
    dans la classe fille pour que la fonction virtuelle ne soit plus cachée.

    Néanmoins le compilateur de C++ builder 2010 me jette lorsque j'ajoute cette ligne de code. Sachant que j'ai également essayé d'ajouter :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using CDbfIO<T>::readAllRecords( map<AnsiString,T> & mapData );
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using CDbfIO<CDataService>::readAllRecords( map<AnsiString,CDataService> & mapData );
    Pensez-vous que c'est une erreur syntaxe de ma part ou une limitation du compilateur de C++ builder 2010 ?

    A noter que pour l'instant je m'en suis tiré en déclarant, dans IDbfIO, les fonctions :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    virtual void readAllRecordsInVector( vector<T> & aData )
    virtual void readAllRecordsInMap( map<AnsiString,T> & mapData )
    au lieu de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    virtual void readAllRecords( vector<T> & aData )
    virtual void readAllRecords( map<AnsiString,T> & mapData )
    Mais je trouve cette solution lourde car elle m'oblige à créer une nouvelle fonction pour chaque type de conteneur alors que je voulais juste surchargé la fonction readAllRecords().

    Merci d'avance !

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    La fonction virtuelle de la classe fille doit avoir la même signature que la classe mère, sans quoi elle est effectivement cachée !
    Donc ce devrait être :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    //CDbfService 
    virtual void readAllRecords( vector<CDataService> & aData );
    Ou faire 2 fonctions virtuelles comme tu l'as fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    virtual void readAllRecordsInVector( vector<T> & aData )
    virtual void readAllRecordsInMap( map<AnsiString,T> & mapData )
    Comme les fonctions génériques ne peuvent être virtuelles, tu ne peux malheureusement pas faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<template<typename > cont
    virtual void readAllRecordsInVector( cont<T> & aData ); // erreur : ne compile pas !!
    Voir : Les fonctions virtuelles en C++ : Fonctions virtuelles et fonctions génériques (template)


    Une solution intéressante peut être de passer par une classe trait pour ta classe de base spécifiant le type effectif utilisé (code à la volé) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <class T, typename traits_t_>
    class IDbfIO
    {
    public:
    	/// \brief Lit tous les enregistrements dans un vecteur
    	virtual void readAllRecords( typename traits_t::type_read_all & aData )
    Puis :
    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
     
    template<class T1,T2>
    struct	map_trait
    {
        typedef std::map<T1,T2> type_read_all;
    };
    class CDbfService : public IDbfIO<CDataService,map_trait<AnsiString,CDataService> >
    {
    public:
    	CDbfService();
    	~CDbfService();
     
    	/// \brief Specialisation de lecture pour les CDataService
    	/// \param data : donnees du service
    	virtual void readAllRecords( std::map<AnsiString,CDataService& aData );
    };
    Sachant que si tout est déterminé à la compilation, alors il est peut être intéressant de mettre les fonctions en policy. Cf Classes traits et politiques par Alp

    Autre lecture qui peut être intéressante : F.A.Q : Qu'est-ce que le CRTP ?

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    7
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mars 2010
    Messages : 7
    Points : 12
    Points
    12
    Par défaut
    Merci beaucoup pour cette réponse.

    Je pense finalement passer par un allègement de mon architecture. En effet, je pense supprimer le template au niveau de ma classe IDbfIO car niveau factorisation du code même si c'est puissant c'était plus pour implémenter un cas d'école.... ( on se fait plaisir comme on peut ).

    Néanmoins cette architecture m'obligeait par exemple à supplanter la fonction readAllRecords() si je voulais la spécialiser. J'aimerais donc pouvoir garder ce mécanisme afin d'avoir des classes filles ayant des noms de fonctions homogènes.

    J'aimerais donc avoir le mécanisme suivant :

    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
     
    class CMere
    {
          template <class T>
          virtual readAllRecords( map<T> & data );
    };
     
    class CFille1
    {
         readAllRecords( map<CData1> & data );
    };
     
    class CFille2
    {
         readAllRecords( map<CData2> & data );
    };
    A noter que CData1 et CData2 n'ont rien en commun.

    Néanmoins d'après tes dires, ce mécanisme est impossible en C++. As-tu donc une idée d'architecture logicielle qui me permettrait d'avoir ce fonctionnement ?

    Je me souviens, dans un autre cas, avoir résout ce problème en faisant hériter CData1 et CData2 d'une interface IData. Puis en implémentant la fonction :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class CFille2
    {
         readAllRecords( map<IData> & data );
    };
    avec un dynamic_cast vers CData2 dans le corps de la méthode. Néanmoins je trouve cette solution plutôt inélégante pour ne pas dire dégueulasse .

    Merci d'avance !

  4. #4
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    virtual et template font rarement bon ménage. Ici, as tu besoin d'un héritage ou un composant générique ne serait-il pas suffisant ?

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Joel F Voir le message
    virtual et template font rarement bon ménage.
    Et même carrément, comme présenté ici, sont impossibles : une fonction générique ne peut être virtuelle.
    @benbarate : sans plus d'éléments, je vois différentes possibilités :
    -> les classes politiques&traits ;
    -> le CRTP ;
    -> le pimpl idiom ou un DP de type délégation.

  6. #6
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par benbarate Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template <class T>
    class IDbfIO
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    class CDbfService : public IDbfIO<CDataService>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using CDbfIO<CDataService>::readAllRecords( map<AnsiString,CDataService> & mapData );
    Pensez-vous que c'est une erreur syntaxe de ma part ou une limitation du compilateur de C++ builder 2010 ?

    D'où vient le CDbfIO? Je n'ai vu que des IDbfIO avant.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using IDbfIO<CDataService>::readAllRecords;
    Devrait faire ce qu'il faut.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

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

Discussions similaires

  1. fonction virtuelle cachée
    Par Kalite dans le forum C++
    Réponses: 6
    Dernier message: 18/07/2011, 09h03
  2. class template et fonction amie
    Par sleepless dans le forum C++
    Réponses: 5
    Dernier message: 19/11/2010, 13h37
  3. Réponses: 24
    Dernier message: 15/03/2007, 15h43
  4. [Classes] - Fonctions virtuelles et dérivées
    Par buzzkaido dans le forum C++
    Réponses: 2
    Dernier message: 18/01/2007, 21h51
  5. Problèmes de fonctions membres de classe templates, gcc3.3.6
    Par yves.dessertine dans le forum Autres éditeurs
    Réponses: 12
    Dernier message: 17/10/2005, 21h36

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