web-dev-qa-db-fra.com

Comment puis-je simuler un polymorphisme de style OO en C?

Existe-t-il un moyen d'écrire un code de type OO dans le langage de programmation C?


Voir également:

Trouvé en cherchant sur "[c] oo".

62
prinzdezibel

Le premier compilateur C++ ("C avec classes") générerait du code C, donc c'est faisable.

Fondamentalement, votre classe de base est une structure; Les structures dérivées doivent inclure la structure de base à la première position, de sorte qu'un pointeur sur la structure "dérivée" sera également un pointeur valide sur la structure de base.

typedef struct {
   data member_x;
} base;

typedef struct {
   struct base;
   data member_y;
} derived;

void function_on_base(struct base * a); // here I can pass both pointers to derived and to base

void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class

Les fonctions peuvent faire partie de la structure en tant que pointeurs de fonction, de sorte qu'une syntaxe telle que p-> call (p) devienne possible, mais vous devez toujours explicitement passer un pointeur sur la structure vers la fonction elle-même.

56
UncleZeiv

Une approche commune consiste à définir une structure avec des pointeurs vers des fonctions. Ceci définit des 'méthodes' qui peuvent être appelées sur n'importe quel type. Les sous-types définissent ensuite leurs propres fonctions dans cette structure commune et les renvoient.

Par exemple, dans le noyau Linux, il y a struct:

struct inode_operations {
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
    struct dentry * (*lookup) (struct inode *,struct dentry *, 
                               struct nameidata *);
    ...
};

Chaque type de système de fichiers enregistré enregistre ensuite ses propres fonctions pour create, lookup et les fonctions restantes. Le reste du code peut ensuite utiliser des opérations génériques inode_operations:

struct inode_operations   *i_op;
i_op -> create(...);
50
Peter Štibraný

C++ n'est pas si loin de C.

Les classes sont des structures avec un pointeur masqué sur une table de pointeurs de fonction appelée VTable. La Vtable elle-même est statique . Lorsque les types pointent sur Vtables avec la même structure mais où les pointeurs désignent d'autres implémentations, vous obtenez un polymorphisme.

Il est recommandé d'encapsuler la logique des appels dans une fonction prenant la structure en paramètre pour éviter l'encombrement du code.

Vous devez également encapsuler les structures d'instanciation et d'initialisation dans des fonctions (cela équivaut à un constructeur C++) et une suppression (destructeur en C++). Ce sont de bonnes pratiques quand même.

typedef struct
{
   int (*SomeFunction)(TheClass* this, int i);
   void (*OtherFunction)(TheClass* this, char* c);
} VTable;

typedef struct
{
   VTable* pVTable;
   int member;

} TheClass;

Pour appeler la méthode:

int CallSomeFunction(TheClass* this, int i)
{
  (this->pVTable->SomeFunction)(this, i);
}
26
thinkbeforecoding

J'ai regardé les réponses de tout le monde et j'ai trouvé ceci:

#include <stdio.h>

typedef struct
{
    int (*get)(void* this);
    void (*set)(void* this, int i);
    int member;

} TheClass;

int Get(void* this)
{
    TheClass* This = (TheClass*)this;
    return This->member;
}

void Set(void* this, int i)
{
    TheClass* This = (TheClass*)this;
    This->member = i;
}

void init(TheClass* this)
{
    this->get = &Get;
    this->set = &Set;
}

int main(int argc, char **argv)
{
    TheClass name;
    init(&name);
    (name.set)(&name, 10);
    printf("%d\n", (name.get)(&name));
    return 0;
}

J'espère que cela répond à quelques questions. 

13
red_hax0r

L’annexe B de l’article Open Reusable Object Models , de Ian Piumarta et Alessandro Warth de VPRI est une implémentation d’un modèle Object en GNU C, contenant environ 140 lignes de code. C'est une lecture fascinante!

Voici la version non mise en cache de la macro qui envoie des messages aux objets, en utilisant une extension GNU en C (expression de déclaration):

struct object;

typedef struct object *oop; 
typedef oop *(*method_t)(oop receiver, ...);

//...

#define send(RCV, MSG, ARGS...) ({ \ 
    oop r = (oop)(RCV); \ 
    method_t method = _bind(r, (MSG)); \ 
    method(r, ##ARGS); \ 
}) 

Dans le même document, consultez les structures object, vtable, vtable_delegated et symbol et les fonctions _bind et vtable_lookup.

À votre santé!

12

Les fonctions de fichier fopen, fclose, fread sont des exemples de code OO en C. Au lieu des données privées de la classe, elles fonctionnent sur la structure FILE utilisée pour encapsuler les données et les fonctions C agissent comme une classe membre. functions . http://www.Amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016

1
Priyank Bolia

D'après Wikipedia: Dans les langages de programmation et la théorie des types, le polymorphisme (du grec πολύς, polys, "many, much" et μορφή, morphē, "forme, forme") est la fourniture d'une interface unique à des entités de types différents.

Je dirais donc que le seul moyen de l'implémenter en C consiste à utiliser des arguments variadiques ainsi qu'une gestion (semi) automatique des informations de type . Par exemple, en C++, vous pouvez écrire (pardon pour la trivialité):

void add( int& result, int a1, int a2 );
void add( float& result, float a1, float a2 );
void add( double& result, double a1, double a2 );

En C, parmi d’autres solutions, voici ce que vous pouvez faire de mieux:

int int_add( int a1, int a2 );
float float_add( float a1, fload a2 );
double double_add( double a1, double a2 );

void add( int typeinfo, void* result, ... );

Alors vous avez besoin de:

  1. implémenter le "typeinfo" avec enums/macros
  2. pour implémenter cette dernière fonction avec stdarg.h
  3. dire au revoir à la vérification de type statique C

Je suis presque sûr que toute autre implémentation de polymorphisme devrait ressembler beaucoup à celle-là même… .. Les réponses ci-dessus semblent plutôt essayer de traiter l'héritage plus que le polymorphisme!

0
EnzoR

Pour construire également la fonctionnalité OO en C, vous pouvez consulter les réponses précédentes. 

Mais, (comme cela a été demandé dans d'autres questions réorientées vers celle-ci), si vous voulez comprendre ce qu'est le polymorphisme, à l'aide d'exemples en langage C. Je me trompe peut-être, mais je ne vois rien de plus facile à comprendre que l'arithmétique en pointeurs C. À mon avis, l'arithmétique pointeur est polymorphe par nature dans C Dans l'exemple suivant, la même fonction (méthode dans OO), à savoir l'addition (+), produira un comportement différent en fonction des propriétés des structures en entrée.

Exemple: 

double a*;
char str*;

a=(double*)malloc(2*sizeof(double));
str=(char*)malloc(2*sizeof(char)); 

a=a+2; // make the pointer a, point 2*8 bytes ahead.

str=str+2; // make the pointer str, point 2*1 bytes ahead.

Disclaimer: Je suis très nouveau chez C et j'ai très hâte d'être corrigé et d'apprendre des commentaires des autres utilisateurs, voire même d'effacer complètement cette réponse si elle se trompait. Merci beaucoup,

0
johnnybegood
#include <stdio.h>

typedef struct {
    int  x;
    int z;
} base;

typedef struct {
    base;
    int y;
    int x;
} derived;

void function_on_base( base * a) // here I can pass both pointers to derived and to base
{
    printf("Class base [%d]\n",a->x);
    printf("Class base [%d]\n",a->z);
}
void function_on_derived( derived * b) // here I must pass a pointer to the derived class
{
    printf("Class derived [%d]\n",b->y);
    printf("Class derived [%d]\n",b->x);
}

int main()
{
    derived d;
    base b;
    printf("Teste de poliformismo\n");

    b.x = 2;
    d.y = 1;
    b.z = 3;
    d.x = 4;
    function_on_base(&b);
    function_on_base(&d);
    function_on_derived(&b);
    function_on_derived(&d);
    return 0;
}

La sortie était:

Class base [3]
Class base [1]
Class base [4]
Class derived [2]
Class derived [3]
Class derived [1]
Class derived [4]

donc ça marche, c'est un code polymorphe.

UncleZeiv a expliqué à ce sujet au début.

0
Paulo Silva