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 :

Quelle est la meilleure manière de faire de la POO en C ?


Sujet :

C

  1. #1
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut Quelle est la meilleure manière de faire de la POO en C ?
    Salut à tous ! Je recherche actuellement la meilleure méthode pour créer une bibliothèque de programmation orientée objets en C.
    Je cherche depuis longtemps, et j'ai lu beaucoup de code dans beaucoup de langage. Au final, je voudrais une implémentation qui ressemble à ce que l'on peut trouver en objective-C, avec les messages, les protocoles, l'héritage unique et surtout les fonctions retain () et release () ... Tout devrait aussi être thread-safe ( En utilisant l'API pthread). Aujourd'hui, j'attends vos idées, sachant que je veux pouvoir accéder aux objets de cette façon: "obj-> vptr-> write (obj, stdout)".
    Il serait également souhaitable de pouvoir vérifier l'intégrité des objets, et de fournir un mécanisme privé / public / protégé (Une idée intéressante est la suivante: https://c.developpez.com/cours/regles-programmation- orientee-objet-c /? page = page_2 # LII-G).
    Mais mon plus gros problème est de faire l'héritage d'une manière efficace et propre (comme je développe une bibliothèque, il serait souhaitable d'avoir une classe de base (comme NSObject) qui fournit les fonctions obligatoires (retain (), release (), copy (), ...)) ... Je sais que beaucoup vont me dire "passe au C++", ou "utilise des choses existantes", mais je répondrais que je préfère le C aux autres langages, et que j'ai besoin de quelque chose de portable (donc on oublie GObject et tout le tralala), donc merci de m'aider au lieu d'essayer de me convaincre que c'est pas la bonne manière de faire.
    Je remercie tout le monde par l'avance !

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 670
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 670
    Points : 10 677
    Points
    10 677
    Par défaut
    Citation Envoyé par ghost2002 Voir le message
    un mécanisme privé / public / protégé
    Cela on peut le faire sauf pour l'héritage via 1) les pointeurs opaques (avec 1 redirection en plus) 2) en séparant dans 2-X fichiers et en faisant les bon include (les fichiers privées incluent les fichiers publics)

    Citation Envoyé par ghost2002 Voir le message
    Mais mon plus gros problème est de faire l'héritage d'une manière efficace et propre
    Impossible en C. L'héritage en C est simple : on redéfinie les classes filles avec les données membres de la classe mère dans le même ordre et on ajoute à la fin.
    Et ensuite, c'est les pointeurs et/ ou le slicing qui prennent le relais

  3. #3
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut
    Salut et merci de ta réponse ! Les pointeurs opaques ne sont pas une solution, étant donner que la structure représentant l'objet contient la table d’accès aux fonctions. Et si il est pas possible de faire de l’héritage, n'y a t'il pas au moins moyen de faire un objet de base, comme dit précedemment, spécialement conçu pour être hérite et fournir les fonctions importantes ? Les devellopeurs de GObject ou COS devait bien avoir des idées... J'ai essayer de lire le code, mais je le trouve extrêmement complexe...

    PS: c'est quoi le slicing ?

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 128
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 128
    Points : 33 053
    Points
    33 053
    Billets dans le blog
    4
    Par défaut
    Tu peux toujours tenter un héritage à base de composition mais ça n'a pas la puissance d'un héritage réel que d'autres langages fournissent.
    Sinon tu prends l'approche Python (l'implémentation, pas le langage) avec des structures bien foutues pour gérer ça et des cast à tout va. Mais c'est vite difficile à lire.

    Mais en l'état, ton projet est plus proche de l'utopie. Tu en demandes clairement trop, et en plus d'être thread-safe..
    Autant faire du C++, là tu pourras créer ton héritage comme tu le souhaites.
    Et surtout les utilisateurs pourront également créer leur surcouche héritée de ta lib.

    Tu peux créer une API portable en C par-dessus une bibliothèque écrite en C++. Et c'est sûrement ce que je ferais.

  5. #5
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut
    Désolé, mais là va me falloir des exemples... C'est quoi "héritage à base de composition" ? A quoi ressemble l'approche python ? Et enfin, comment on fait une surcouche sur du C++ ? Je sais que ça marche dans l'autre sens avec "extern "C" {}", mais C++ vers C ?
    En fait je comprend rien dans ton message

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    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 483
    Points : 13 681
    Points
    13 681
    Billets dans le blog
    1
    Par défaut
    Je vais peut-être passer pour un troll mais je vais le dire quand même : passe au C++.

    Si ta seule raison de rester en C c'est "j'aime le C", j'ai une nouvelle pour toi : mais le C++ c'est comme le C ! Sauf que tout ce que tu souhaites faire est déjà fourni dans le langage. Tu auras une meilleure syntaxe, un support du compilateur, pas de bug, bref que du plus.

    Si ta seule raison de rester en C c'est "j'aime le C", c'est une mauvaise raison. Ne pas avoir de compilateur C++ pour ta cible serait une bonne raison.

    Après si tu aimes de faire mal, c'est ton choix La question est peut-être : pourquoi vouloir faire de l'objet avec un langage clairement pas fait pour ça ?

  7. #7
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut
    Bah ma raison c'est la plus grande stabilité et maturité du C (plus de plateformes, plus optimisé...) (et aussi que je préfère, c'est vrai). Ensuite, je veux une logique objet parce que tout les langages modernes en dispose, et que je trouve très agréable de travailler avec ça. De plus, cette conception étant arrivée après les premiers langages, elle est le fruit d'une longue réflexion, signe qu'elle ne peut qu'être bénéfique. Logique non ?

  8. #8
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    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 483
    Points : 13 681
    Points
    13 681
    Billets dans le blog
    1
    Par défaut
    Tu penses vraiment que le C++ est si peu mature comparé au C ?

    Il faut vraiment voir quelles sont les cibles que tu vises pour justifier l'argument "plus de plateformes, plus optimisé". Beaucoup de plateformes ont du support pour C++. Et c'est peut-être suffisant pour ce que tu veux faire. Par exemple, GCC est porté sur énormément de plateformes par exemple et supporte très le C++ (*) : je l'ai utilisé sur PC, sur ARM Cortex-M (MCU 32 bits), sur Texas Instruments MSP430 (MCU 16 bits), pour faire du C++11 et plus. Quand à "plus optimisé", il ne faut pas perdre de vue que tu vas recoder en fait en C une partie de ce que fait un compilateur C++, par exemple les vtables pour faire des fonctions virtuelles. Penses-tu que tu feras mieux que les gens qui font des compilateurs ? Sincèrement, et sans te connaitre ni te juger, j'en doute Un pote m'a raconté que sur un projet pro, lui et son équipe ont refait les mécanismes types vtable pour faire de l'OO en C car ils ne pouvaient faire de C++. Son bilan ? C'était moins beau et moins optimisé qu'en C++...

    Je comprends tout à fait que tu souhaites faire de l'OO. Je code en OO pour des projets embarqués sur MCU depuis des années. Je l'ai fait en Java et maintenant en C++. Et sincèrement depuis que je fais du C++ je n'imagine plus faire du C. Tout ce que je peux faire en C, je peux le faire en C++. Et ce que je ne peux pas faire en C, j'ai plus de solutions pour le faire en C++.

    (*) Il y a déjà plus années, GCC a choisi d'utiliser C++ au lieu de C pour s'implémenter : https://lwn.net/Articles/542457/ Quand un compilateur C / C++ est codé en C++, on peut se demander si la partie C est vraiment meilleure que la partie C++, non ? L'article dit quelque chose de très juste de toute façon :
    The C subset of C++ is as efficient as C.

  9. #9
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut
    Arguments très convaincants. Particulièrement celui au sujet de GCC (ce que j'ignorais). Et tu as raison, je n'ai pas le niveau pour faire quelque chose d'encore plus optimiser. Mais je suis têtu (je sais c'est pas bien et stupide), et je voudrais, ne serait-ce que par curiosité, savoir comment faire.

    Un pote m'a raconté que sur un projet pro, lui et son équipe ont refait les mécanismes types vtable pour faire de l'OO en C car ils ne pouvaient faire de C++. Son bilan ? C'était moins beau et moins optimisé qu'en C++...
    As-tu une idée de comment ils ont fait ?

  10. #10
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    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 483
    Points : 13 681
    Points
    13 681
    Billets dans le blog
    1
    Par défaut
    Et tu as tout à fait raison de vouloir tenter C'est forcément intéressant à faire pour le challenge, mais je ne pense pas que ce soit une alternative crédible au C++ si tu as le droit de faire du C++ sur ton projet. Simplement par la complexité à implémenter : ça va te demander du temps et c'est du temps que tu ne passeras à réellement coder ton projet.

    As-tu une idée de comment ils ont fait ?
    Je n'en ai aucune idée pour être honnête...

  11. #11
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut
    Alors tu peux peut-être me parler de la manière dont TOI tu procéderais ?

  12. #12
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    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 483
    Points : 13 681
    Points
    13 681
    Billets dans le blog
    1
    Par défaut
    Je ferais du C++

    Blague à part, j'ai déjà lu des tuto sur le sujet mais je n'ai jamais implémenté ça. Donc j'irais fouillé le net et j'essayerai de trouver une implémentation qui existe et qui fonctionne.

  13. #13
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 128
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 128
    Points : 33 053
    Points
    33 053
    Billets dans le blog
    4
    Par défaut
    Une vtable n'est jamais qu'un tableau de pointeurs de fonctions.
    Pour le reste, quelles sont tes compétences réelles en C ? Penses-tu vraiment pouvoir faire ton truc ?
    Selon moi, et vues les questions que tu poses, ce n'est pas à ta portée actuellement. Pas en C. (en plus d'être un choix que je trouve également stupide pour ça, puisque ça revient comme dit plus haut à implémenter une partie d'un compilateur C++)

  14. #14
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut
    Bktero, ça serait miraculeux que tu trouves un truc, parce que je pense avoir éplucher absolument tout ce qui existe sur le sujet (même un article en chinois ). Bousk, je sais ce qu'est une vtable, et j'ai déjà fait plusieurs implémentation, mais aucune dont je soit satisfait. C'est la raison de ma présence ici... Concernant mon niveau en C, je ne jugerais pas objectivement, mais je pense commençais à me débrouiller. Ca fait cinq ans que je bricole et lit du code...

  15. #15
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 580
    Points : 7 712
    Points
    7 712
    Par défaut
    Bonjour,

    Voilà un exemple de vtable le plus simple (héritage direct et unique)
    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
    struct Animal {
    	const struct AnimalVTable* vtable;
    	char nom[10];
    	#define AnimalCrier(panimal)  (*(panimal)->vtable->crier)(panimal)
    	#define AnimalCreer(panimal,n) { (panimal)->vtable = &vTableAnimal; strcpy(nom,n); }
    };
     
    void animal_crier( struct Animal* an ) {
    	printf( "je suis %s qui crie\n" , an->nom );
    }
     
    struct AnimalVTable {
    	void (*crier)(struct Animal*);
    } const vTableAnimal = {
    	&animal_crier
    };
     
    struct Chat { // Le Chat est un Animal
    	struct Animal animal;
     
    	#define ChatCrier(pchat)  (*(pchat)->animal.vtable->crier)((struct Animal*)(pchat))
    	#define ChatCreer(pchat,n)   { (pchat)->animal.vtable = (struct AnimalVTable const*)&vTableChat; strcpy(animal.nom,n); }
    };
     
    void chat_crier( struct Animal* an ) {
    	printf( "je suis %s qui miaule\n" , an->animal.nom );
    }
     
    struct ChatVtable {
    	void( *crier )(struct Animal*);
    } const vTableChat = {
    	&chat_crier
    };
     
    int main() {
    	struct Chat  chat;
    	ChatCreer( &chat , "mickey" );
    	ChatCrier( &chat );
     
    	struct Animal* animalInconnu = &chat;
    	AnimalCrier( animalInconnu );
    }
    En C++, ça s'écrit
    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
    struct Animal {
        Animal( std::string nom) : nom(nom) {}
        virtual void crier() { std::cout << "je suis " << nom << " qui crie\n" ); }
        std::string  nom;
    };
     
    struct Chat  :  Animal {
        Chat( std::string nom) : Animal(nom) {}
        void crier()override { std::cout << "je suis " << nom << " qui miaule\n" ); }
    };
     
    int main() {
    	Chat  chat("Mickey");
    	chat.crier();
     
    	Animal* animalInconnu = &chat;
    	animalInconnu->crier();
    }

  16. #16
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut
    Bonjour ! Je comprend pas trop pourquoi les #define dans les structures. Et tu utilise des fonctions auxquelles tu passe une structure ("ChatCreer( &chat , "mickey" );, ..."), mais je voudrais que les fonctions soit membres de l'objet ("obj->vptr->write(obj, stdout)")...

    PS: C'est quoi les variantes plus compliquées s'il te plaît ?

  17. #17
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 580
    Points : 7 712
    Points
    7 712
    Par défaut
    Les #define sont pour "simplifier" les appels de fonction (sinon il a des des cast, des translations et des indirections à effectuer.) C'est ce à quoi j'ai pensé mais je n'ai pas de solution simple, je suis comme les autres je n'ai trouvé de solution viable en C, y compris Bjarne Stroustrup qui a du inventer un nouveau langage.
    On ne peut pas avoir des fonctions membres de l'objet. On peut au mieux avoir des pointeurs sur des fonctions qui sont dans la vtable.
    Les variantes plus compliquées sont nécessaires pour gérer l'héritage multiple (interdit par certains langages qui doivent ajouter le concept d'Interface) et l'héritage virtuel (souvent imposé par certains langages); Dans ces cas la vtable n'est pas gérée par un simple pointeur sur une table, mais plusieurs pointeurs sur des pré-tables qui renvoient à des tables de pointeurs.

  18. #18
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 488
    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 488
    Points : 6 177
    Points
    6 177
    Par défaut
    Ton code ne compile pas, dalfab.

    Allez, pour m'amuser, je viens d'écrire un petit bout de code en C qui illustre 3 manières de faire du polymorphisme :
    • de la programmation orientée objet dans laquelle chaque objet contient un pointeur vers une table virtuelle ;
    • de la programmation orientée protocole dans laquelle la table virtuelle est retirée de l'objet et mise dans une structure qui contient le pointeur vers l'objet ;
    • de la programmation générique à coup de macros.


    Le code suivant compile à la fois en C et en C++ :
    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
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    #include <stdio.h>
     
    typedef enum {
    	LoggerLevel_Error,
    	LoggerLevel_Warning,
    	LoggerLevel_Info,
    	LoggerLevel_Debug
    } LoggerLevel;
     
    /////////////////////////////////
    // Object oriented programming //
    /////////////////////////////////
     
    // Base class
     
    struct LoggerVTable;
     
    typedef struct LoggerBase {
    	struct LoggerVTable* m_vtable;
    } LoggerBase;
     
    typedef struct LoggerVTable {
    	void(*m_log)(LoggerBase* self, const char* msg, LoggerLevel level);
    } LoggerVTable;
     
    void LoggerLog(LoggerBase* self, const char* msg, LoggerLevel level) {
    	self->m_vtable->m_log(self, msg, level);
    }
     
    // Derived class
     
    typedef struct LoggerDeriv {
    	LoggerBase super;
    	FILE* m_file;
    } LoggerDeriv;
     
    void LoggerDerivLog(LoggerDeriv* self, const char* msg, LoggerLevel level) {
    	fprintf(self->m_file, "%s\n", msg);
    }
     
    void LoggerDerivLogWithCast(LoggerBase* self, const char* msg, LoggerLevel level) {
    	LoggerDerivLog((LoggerDeriv*) self, msg, level);
    }
     
    LoggerVTable loggerDerivVTable = { &LoggerDerivLogWithCast };
     
    void LoggerDerivCtor(LoggerDeriv* self, FILE* file) {
    	self->super.m_vtable = &loggerDerivVTable;
    	self->m_file = file;
    }
     
    // Demo
     
    void firstDemo() {
    	LoggerDeriv logger;
    	LoggerDerivCtor(&logger, stdout);
    	LoggerBase* loggerPtr = &logger.super;
    	LoggerLog(loggerPtr, "Object oriented programming.", LoggerLevel_Debug);
    }
     
    ///////////////////////////////////
    // Protocol oriented programming //
    ///////////////////////////////////
     
    // Simple class
     
    typedef struct SimpleLogger {
    	FILE* m_file;
    } SimpleLogger;
     
    void SimpleLoggerCtor(SimpleLogger* self, FILE* file) {
    	self->m_file = file;
    }
     
    void SimpleLoggerLog(SimpleLogger* self, const char* msg, LoggerLevel level) {
    	fprintf(self->m_file, "%s\n", msg);
    }
     
    // Protocol
     
    typedef struct LoggerHandle {
    	void* m_obj;
    	void(*m_log)(void* obj, const char* msg, LoggerLevel level);
    } LoggerHandle;
     
    void LoggerHandleLog(LoggerHandle self, const char* msg, LoggerLevel level) {
    	self.m_log(self.m_obj, msg, level);
    }
     
    // Binding between SimpleLogger and LoggerHandle
     
    void SimpleLoggerLogWithCast(void* self, const char* msg, LoggerLevel level) {
    	SimpleLoggerLog((SimpleLogger*) self, msg, level);
    }
     
    void LoggerHandleCtorSimpleLogger(LoggerHandle* self, SimpleLogger* obj) {
    	self->m_obj = (void*) obj;
    	self->m_log = &SimpleLoggerLogWithCast;
    }
     
    // Demo
     
    void secondDemo() {
    	SimpleLogger logger;
    	SimpleLoggerCtor(&logger, stdout);
    	LoggerHandle loggerHandle;
    	LoggerHandleCtorSimpleLogger(&loggerHandle, &logger);
    	LoggerHandleLog(loggerHandle, "Protocol oriented programming.", LoggerLevel_Debug);
    }
     
    /////////////////////////
    // Generic programming //
    /////////////////////////
     
    #define DefineSomeFunctions(loggerType) \
    	void loggerType ## LogTwice(loggerType* self, const char* msg, LoggerLevel level) { \
    		loggerType ## Log(self, msg, level); \
    		loggerType ## Log(self, msg, level); \
    	}
     
    // Demo
     
    DefineSomeFunctions(LoggerDeriv)
    DefineSomeFunctions(SimpleLogger)
     
    void thirdDemo() {
    	{
    		LoggerDeriv logger;
    		LoggerDerivCtor(&logger, stdout);
    		LoggerDerivLogTwice(&logger, "Generic programming with LoggerDeriv.", LoggerLevel_Debug);
    	}
    	{
    		SimpleLogger logger;
    		SimpleLoggerCtor(&logger, stdout);
    		SimpleLoggerLogTwice(&logger, "Generic programming with SimpleLogger.", LoggerLevel_Debug);
    	}
    }
     
    int main() {
    	firstDemo();
    	secondDemo();
    	thirdDemo();
    	return 0;
    }
    En langage C, il faut tout bricoler à la main. Pédagogiquement, cela peut être intéressant. Par contre, si on a besoin d'être productif, il vaut mieux se tourner vers un autre langage (si c'est possible).

    PS : remarque sur la partie orientée objet : j'ai limité l'implémentation au plus simple. Si la classe de base a un état ou si la classe dérivée contient plus de fonctions virtuelles que la classe de base, alors il faut complexifier le code. Si on veut de l'héritage multiple, alors il faut complexifier encore plus. Si on veut du RTTI, il faut complexifier aussi.

    Edit 2018-07-06-19h44 : LoggerHandleLog : LoggerHandle passé par valeur au lieu de par pointeur.
    Edit 2018-07-06-19h51 : mention du RTTI.
    Edit 2018-07-06-21h01 : lapsus corrigé dans mon commentaire "Edit 2018-07-06-19h44".
    Edit 2018-07-07-11h38 : retrait de deux casts.

  19. #19
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2018
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juillet 2018
    Messages : 28
    Points : 22
    Points
    22
    Par défaut
    merci a tous ! Je suis en train d'étudier les codes... Et c'est quoi le RTTI ?

    PS: Qu'est-ce qui empêche de faire ça:
    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
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
     
    //Test.h
    struct _test_vtable
    {
          void(*print)(struct Test *);
          void(*set)(struct Test *, int);
          void(*dtor)(struct Test *);
    }
     
    struct Test
    {
          struct _test_vtable const *vptr;
    };
    typedef struct Test Test;
     
    Test *test_ctor(void);
     
    //Test.c
    #include <stdio.h>
    #include <stdlib.h>
     
    #include "Test.h"
     
    struct _test_private_vtable
    {
          void(*print)(struct Test_private *);
          void(*set)(struct Test_private *, int);
          void(*dtor)(struct Test_private *);
    }
     
    struct Test_private
    {
          struct _test_private_vtable const *vptr;
          int i;
    };
    typedef struct Test_private Test_private;
     
    void test_print(struct Test_private *This)
    {
           if (This)
           {
                   printf("%d\n", This->i);
           }
    }
     
    void test_set(struct Test_private *This, int i)
    {
           if (This)
           {
                  This->i = i;
           }
    }
     
    void test_dtor(struct Test_private *This)
    {
           if (This)
           {
                   free(This);
                   This = NULL;
           }
    }
     
    Test *test_ctor(void)
    {
          static struct _test_private_vtable const vtable = {
                   &test_print,
                   &test_set,
                   &test_dtor
           };
     
          Test_private *ret = malloc(sizeof(*ret));
          if (ret)
          {
                ret->vptr = &vtable;
                ret->i = 0;
                return(ret);
          }
     
          return(NULL);
    }
     
    //main.c
    #include <stdio.h>
    #include <stdlib.h>
     
    #include "Test.h"
     
    int main(int argc, char *argv[])
    {
          Test *obj = test_ctor();
           if (obj)
           {
                 obj->vptr->print(obj);
                 obj->vptr->set(obj, 25);
                 obj->vptr->print(obj);
                 obj->vptr->dtor(obj);
           }
     
          return(0);
    }
    Bien sûr les casts sont implicites...

  20. #20
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 670
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 670
    Points : 10 677
    Points
    10 677
    Par défaut
    Citation Envoyé par ghost2002 Voir le message
    Et c'est quoi le RTTI ?
    C'est une notion C++ "Run-Time Type Identification" : lien wiki en pour aller plus vite
    En résumé dynamic_cast<>, typeid, std::type_info.


    Mais c'est juste un petit ensemble de la réflexion (<- lien wiki fr)
    Justement, en Objective-C pour prendre un exemple, tu peux tester si un objet a une méthode ou pas : c'est une partie.
    En Java pour un autre exemple, tu peux créer un objet avec le nom d'une classe : c'est une partie.

Discussions similaires

  1. Réponses: 16
    Dernier message: 18/08/2008, 18h29
  2. Quelle est la meilleure maniere de faire une count?
    Par farenheiit dans le forum Administration
    Réponses: 20
    Dernier message: 16/11/2007, 16h45
  3. Réponses: 3
    Dernier message: 09/11/2007, 15h38
  4. Réponses: 2
    Dernier message: 19/03/2007, 16h41
  5. Copie disque à disque. Quelle est la meilleur manière ?
    Par olive_le_malin dans le forum Autres Logiciels
    Réponses: 20
    Dernier message: 19/02/2006, 00h32

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