Bon allez je m'attaque au polymorphisme en C. Bon je sais plein de gens l'ont fait avant moi (cast, macro, ...), mais la petite méthode que je viens juste de mettre au point est assez originale je pense ! (Elle remplace pas les autres, elle est surement bourrée de bugs et de limitations, mais bon, je suis super content d'y être arrivé. Ça me trottait dans la tête depuis des mois (en me disant, ça doit être possible, ça doit être possible) )
Évidemment, ce n'est pas miracle (faut pas rêver, le C++ n'a été conçu que pour ajouter le polymorphisme au C).
Après avoir inclut la librairie de préprocessing :
#include "m-macro.h"
on définit où on veut les prototypes des méthodes (on peut mettre ce qu'on veut mais il faut les définit avant de définir les classes qui les utilisent - normal, non ?) :
/* Define generic INIT method */
#define PROTO_METHOD_INIT(type, alias) void (*alias)(type)
#define INIT(...) M_CALL_METHOD(INIT, __VA_ARGS__)
/* Define generic CLEAR method */
#define PROTO_METHOD_CLEAR(type, alias) void (*alias)(type)
#define CLEAR(...) M_CALL_METHOD(CLEAR, __VA_ARGS__)
puis on définit les classes avec leurs méthodes (exemple avec GMP):
#include <gmp.h>
/* Register new class: mpz_t */
#define TYPE mpz_t
#define CLASS INIT, mpz_init, CLEAR, mpz_clear
#include M_REGISTER_CLASS
/* Register new class: mpq_t */
#define TYPE mpq_t
#define CLASS INIT, mpq_init, CLEAR, mpq_clear
#include M_REGISTER_CLASS
puis on les utilise et la bonne fonction sera appelée automatiquement :
void f(void)
{
mpz_t a;
mpq_t q;
INIT(a);
INIT(q);
CLEAR(a);
CLEAR(q);
}
Cela génère le code suivant sans erreur ni avertissement :
f:
subq $56, %rsp
movq %rsp, %rdi
call __gmpz_init
leaq 16(%rsp), %rdi
call __gmpq_init
movq %rsp, %rdi
call __gmpz_clear
leaq 16(%rsp), %rdi
call __gmpq_clear
xorl %eax, %eax
addq $56, %rsp
ret
Une classe n'a pas à implanter toutes les méthodes existantes, juste celles qu'elle supporte.
C'est surement bourré de limitations, mais c'est C11 compliant (pas d'extension), ça n'utilise aucun cast en interne (tout est type safe) et je suis super content d'y être arrivé
Les méthodes et les classes sont décorrélés (elles peuvent donc être dans des header différents), et çà c'est super cool
Plus fort. Mettons que le code plus loin enregistre une nouvelle classe (donc après que certaines méthodes aient déjà été utilisées):
/* Define generic COPY method */
#define PROTO_METHOD_COPY(type, alias) void (*alias)(type, const type)
#define M_COPY(...) M_CALL_METHOD(COPY, __VA_ARGS__)
/* Register new class: mpf_t */
#define TYPE mpf_t
#define CLASS INIT, mpf_init, CLEAR, mpf_clear, COPY, mpf_set
#include M_REGISTER_CLASS
et ben le code suivant marche !
int g(){
mpf_t a;
INIT(a);
CLEAR(a);
return 0;
}
On génére bien
g:
subq $40, %rsp
movq %rsp, %rdi
call __gmpf_init
movq %rsp, %rdi
call __gmpf_clear
xorl %eax, %eax
addq $40, %rsp
ret
Je ne sais pas si ca me servira mais je suis content d'y être arrivé
Détails sur la méthode d'implantation l'année prochaine
Si vous êtes sages (et êtes curieux de le savoir).