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 :

Utiliser un template sans préciser ses paramètres


Sujet :

C++

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut Utiliser un template sans préciser ses paramètres
    Bonjour,

    J'ai eu du mal à formuler ce titre et j'ai un peu de mal à formuler mon problème... Je vois souvent des intérêts aux templates pour figer la taille de certains éléments à la compilation. Comme je travaille en embarqué, ça me permet de figer à la compilation la mémoire nécessaire et d'éviter l'allocation dynamique. Mon problème est que je vois pas comment utiliser facilement le type template en paramètre ou retour d'autres fonctions sans mettre tout le monde template.

    Un petit code sera plus clair je pense :
    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
    template<unsigned int icon_count>
    class StatusBar
    {
        void setIcon(unsigned int index, bool on)
        {
            // bla bla
        }
     
        unsigned int getIconCount()
        {
            return icon_count;
        }
    };
     
    StatusBar<5> my_bar;
     
    StatusBar<?> getStatusBar()
    {
        return my_bar;
    }
     
    void init()
    {
        StatusBar<?> bar = getStatusBar();
        for (unsigned int i = 0; i < bar.getIconCount(); ++i)
        {
            bar.setIcon(i, false);
        }
    }
    Pour rester plus "standard", on peut faire la même chose avec array de <array>. En admettant qu'on spécialise sur le paramètre de type mais pas sur la taille, on ne peut toujours pas le passer à des fonctions non templates (la taille n'est pas un problème, on peut la récupérer auprès de l'objet).

    Est-il possible de réaliser un tel code ? Si oui, comment ? Si non, il me reste donc à oublier définitivement ce genre d'idées

    Merci d'avance !

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Salut!

    Non, on ne peux pas ne pas préciser complètement la template utilisée sans être template du reste soi-même.
    Il faut bien comprendre qu'une template n'est pas du code concret, et que StatusBar<1> n'est pas la même classe que StatusBar<5>.


    Par contre, tu peux utiliser la déduction automatique de type, avec le mot-clé auto.
    En effet, ce qui est important pour le développeur, ce n'est pas le type précis de la valeur, mais sa signification. Le compilateur connaît très bien son type, c'est son travail.

    Selon la norme que tu utilises, il peut être nécessaire de préciser le "trailing type", voici la syntaxe:
    • C++11: auto getStatusBar() -> decltype(m_bar) { return my_bar; }
    • C++14: auto getStatusBar() { return my_bar; }

    Je ne sais plus sous quelle condition il faut utiliser le trailing type, essaye sans le compilateur râlera s'il est nécessaire.

    De même, dans init(), bar peut être définie par auto bar = getStatusBar(); ou auto const bar = getStatusBar();.

    La règle est que auto est utilisable dans chaque définition où une valeur fournit le type à utiliser.
    Ainsi, il n'est pas possible de déclarer une fonction retournant auto sans la définir immédiatement.

    Cette astuce est également valable dans la définition de template.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template <typename A, typename B> auto add(A a, B b) { return a + b; }

    D'un autre côté, je trouve dérangeant d'avoir un type template dans du code "métier".
    Quand bien même ce serait le type adapté, je préfère de très loin avoir un alias de type portant un nom métier.
    Le type que le métier manipule n'est pas StatusBar<5> mais le StatusBar-qui-va-bien, par exemple DetectorStatusBar.

    En général, dans les applications que je manipule, j'ai visiblement trois couches de types:
    • Les types purement techniques, définis par des templates.
    • Les types auxiliaires du métier, souvent des capsules autour de templates comme vector, mais parfois des typedef vers ces types.
    • Les types purement métier (ou logiques), non template, car la couche métier répond à un besoin précis.


    La frontière entre les deux premières parties est parfois un peu floue, mais quoi qu'il advienne, si j'ai une valeur dont le type est une instance d'une template, je refuse qu'apparaisse son origine template dans le code qui s'en sert.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Par ailleurs, ca me fait tout drôle de répondre à tes questions, après avoir appris autant de tes explications en C.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  4. #4
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut
    Si je puis me permettre d'ajouter quelque chose aux explications super claires du ternel,
    je dirais que face à ce genre de situation ma réponse simple est : héritage !

    Tu déclares une classe (abstraite ou non) dont héritent toutes les instances de ta classe template.
    Voici un petit exemple qui vaut mieux qu'un long charabia :
    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
    struct g_popable
        {
        virtual bool isempty () const = 0 ;
        virtual int pop () = 0 ;
        };
     
    template <unsigned N> class stack : public g_popable
        {
        public:
            bool isempty () const ;
            bool isfull () const ;
            void push ( int x ) ;
            int  pop () ;
        protected:
            int _top,_table [N] ;
        };
     
    int foo ( g_popable & popable )
        {
        if (!popable.isempty())
            return popable.pop() ;
        return -1 ;
        }
     
    int main ()
        {
        stack<30> s1 ;
        stack<20> s2 ;
        s1.push( 5 ) ;
        return foo( s1 ) + foo( s2 ) ;
        }
    Voilà, je ne sais pas si cette solution peut s'appliquer à ton problème, mais moi, je l'aime bien...
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    En effet, tu peux, mais tu paies le prix du virtual, ce qui est dommage.

    Cela dit, la classe de base non template n'est pas une mauvaise idée. Tout particulièrement si on peut y placer un comportement non virtuel.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Effectivement, un code avec auto fonctionne bien :
    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
    template<unsigned int icon_count>
    class StatusBar
    {
    public:
        void setIcon(unsigned int index, bool on)
        {
            // bla bla
        }
     
        unsigned int getIconCount()
        {
            return icon_count;
        }
    };
     
    StatusBar<5> my_bar;
     
    auto getStatusBar() -> decltype(my_bar)
    {
        return my_bar;
    }
     
    void init()
    {
        auto bar = getStatusBar();
        for (unsigned int i = 0; i < bar.getIconCount(); ++i)
        {
            bar.setIcon(i, false);
        }
    }
    Bon le hic c'est que je suis en C++03 mais ceci est une autre histoire

    La réflexion sur les couches logicielles où utiliser des templates et ou ne pas les utiliser est intéressante. Je vais la considérer dans mon design. Surtout que la plupart des cas où je voudrais faire ça est pour fixer des tailles à la compilation, ce que je pourrais faire avec des constantes fournies au code de différentes manières.

    L'idée de la classe d'interface par dessus le template est intéressante aussi. Elle ne résout pas mon problème a priori mais ça me donne un autre angle de vision.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Il faut bien comprendre qu'une template n'est pas du code concret, et que StatusBar<1> n'est pas la même classe que StatusBar<5>.
    Il faut vraiment que je me mette ça dans la tête... Pour l'instant, j'ai la logique Java dans la tête, où les parameterized types sont des sous-types du generic type qu'ils spécialisent.

    Merci pour vos réponses !

  7. #7
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    En C++03, utilise des typedef significatifs. Ca marche très bien comme remplaçant de auto.

    Concernant "les templates ne sont pas des génériques".
    Les templates du C++ sont une méta informations permettant au compilateur de générer des classes et fonctions selon les besoins.

    Pour information, en Java, les génériques sont une seule entité, List est le même fichier.class que List<String> et List<Integer>.
    Les génériques sont vérifiés à la compilation, et remplacés par des casts runtime non vérifiés.
    Si tu lis le bytecode généré pour String s = strings.get(0), le code effectivement présent est "Object s_temporary = call(List.get, 0). s = cast(s_temporary, String)".

    La différence est énorme, et explique pourquoi il n'y a pas de spécialisation en Java, ni de List<int>. On y gagne d'autres choses (j'imagine)
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  8. #8
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 470
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 470
    Points : 6 108
    Points
    6 108
    Par défaut
    Bonjour,

    Soit la fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<size_t icon_count>
    void foo(std::array<Icon, icon_count>& param);
    Pour que cette fonction ne soit plus un template, la solution la plus simple qui n'utilise pas la virtualité est celle-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    //! \param icons pointeur vers le premier élément
    void foo(Icon* icons, size_t icon_count);
    Pour plus d'abstraction, on peut regrouper ces deux paramètres en un seul objet.

    Si j'applique cela à ton exemple, cela donne :
    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
    64
    65
    66
    67
    #include <array>
     
    class Icon
    {
    public:
    	constexpr void setOn(bool on) noexcept {m_on = on;}
    private:
    	bool m_on = false;
    };
     
    template<size_t icon_count>
    class StatusBar
    {
        friend class StatusBarHandle;
    public:
    	constexpr void setIcon(size_t index, bool on) noexcept
    	{
    		m_icons[index].setOn(on);
    	}
    	constexpr static size_t getIconCount() noexcept
    	{
    		return icon_count;
    	}
    private:
    	std::array<Icon, icon_count> m_icons;
    };
     
    class StatusBarHandle
    {
    public:
    	template<size_t icon_count>
    	constexpr explicit StatusBarHandle(StatusBar<icon_count>& statusBar) noexcept :
    		m_icons(statusBar.m_icons.data()),
    		m_icon_count(icon_count)
    	{
    	}
    	constexpr void setIcon(size_t index, bool on) noexcept
    	{
    		m_icons[index].setOn(on);
    	}
    	constexpr size_t getIconCount() const noexcept
    	{
    		return m_icon_count;
    	}
    private:
    	Icon*  m_icons;
    	size_t m_icon_count;
    };
     
    constexpr void reset(StatusBarHandle& statusBarHandle) noexcept
    {
    	for(size_t i = 0; i < statusBarHandle.getIconCount(); ++i)
    	{
    		statusBarHandle.setIcon(i, false);
    	}
    }
     
    int main()
    {
    	StatusBar<3> sb1;
    	StatusBar<5> sb2;
    	StatusBarHandle sbh1(sb1);
    	StatusBarHandle sbh2(sb2);
    	reset(sbh1);
    	reset(sbh2);
    	return 0;
    }
    Edit 2016-01-24-01h02 : ajout d'un "explicit".
    Edit 2016-01-24-02h29 : ajout des "noexcept".

Discussions similaires

  1. Réponses: 11
    Dernier message: 26/02/2015, 01h20
  2. [AC-2007] Utiliser deux champs calculés pour créer un nouveau calcul sans passage de paramètre
    Par shakapouet dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 28/04/2014, 09h24
  3. [2008R2] Utilisation d'une procédure avec ses paramètres contenu une table
    Par bisou007 dans le forum Développement
    Réponses: 0
    Dernier message: 01/07/2013, 17h07
  4. Peu-t-on utiliser DIM sans préciser le type
    Par Henri-gp dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 26/07/2012, 11h11
  5. Réponses: 5
    Dernier message: 31/10/2008, 12h24

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