Un système de plugins en C

L'hiver approche, il serait temps de repilosifier votre fourrure. C'est pour cela que je vous propose d'étudier un système de plugins orgianale en C.

On a donc une application chargée de parser des messages et de les traiter d'une manière ou d'une autre en fonction de leur contenu.

Au bout d'un moment l'appli devient monstrueuse mais on a besoin d'ajouter encore plein de features toussa toussa. On décide donc de la rendre pluggable.
L'orgianalité réside dans le fait qu'on va laisser les plugins “informer” notre appli si c'est à eux de prendre en charge le message ou pas (et non pas l'appli qui décide quel plugin appeler).

En gros :

  • Je reçois un message
  • Je le passe tour à tour à chaque plugin
  • Lorsqu'un plugin rencontre un message qu'il connait, il le traite
  • Fin de la boucle, l'appli peut faire des trucs moins “spécialisés” sur le message


Les codes source qui vont suivre ne sont pas faits pour être beaux, mais pour être simples.
<philosophie_a_deux_roubles> Ce qui ne signifie pas que pour faire simple on doit obligatoirement faire moche. </philosophie_a_deux_roubles>

Je tiens avant tout à présenter mes tartiflettes et mon cassoulet pour la coloration syntaxique de ce wiki qui est, il faut le dire, abominable.

main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
 
typedef int my_func_t( char * foo, char * bar );
 
typedef struct my_plugins_s
{
        char * libname;
        void * handle;
        my_func_t * func;
}
my_plugins_t;
 
my_plugins_t plugins[] = {
        { "libchose.so", NULL, NULL },
        { "libtruc.so", NULL, NULL },
        { NULL, NULL, NULL },
};
 
char * chose = "CHOSE truc chouette";
char * truc  = "TRUC  chose chouette";
char * autre = "AUTRE CHOSE TRUC POUET";
char * rien;
 
        int
openlibs( )
{
        int i;
        char * error;
 
        for ( i=0; plugins[i].libname; ++i )
        {
                plugins[i].handle = dlopen( plugins[i].libname, RTLD_LAZY );
                if ( !plugins[i].handle ) {
                        fprintf( stderr, "dlopen error : %s\n", dlerror() );
                        return -1;
                }
 
                dlerror();
 
                plugins[i].func = dlsym( plugins[i].handle, "check_sc" );
 
                if ((error = dlerror()) != NULL )
                {
                        fprintf( stderr, "dlsym error : %s\n", error );
                        return -1;
                }
        }
 
        return 0;
}
 
        void
closelibs()
{
        int i;
        for ( i=0; plugins[i].handle; ++i)
                dlclose( plugins[i].handle );
}
 
        int
main( int argc, char **argv )
{
        int i;
        puts("Main program starts ...");
 
        if( ! openlibs() )
        {
                my_func_t check_sc;
                puts(" Trying une chose ...");
                for( i=0; plugins[i].func; ++i )
                        (plugins[i].func)( chose, rien );
 
                puts(" Trying un truc ...");
                for( i=0; plugins[i].func; ++i )
                        (plugins[i].func)( truc, rien );
 
                puts(" Trying autre chose ...");
                for( i=0; plugins[i].func; ++i )
                        (plugins[i].func)( autre, rien );
        }
 
        closelibs();
 
        return 0;
 
}
libchose.c
#include <stdio.h>
#include <string.h>
 
        int
check_sc( char * foo, char * bar )
{
        if ( ! strncmp( foo, "CHOSE", 5 ) )
        {       
                puts("   Il y a une chose !");
                return 0;
        }
        puts("   Il n'y avait pas de chose.");
        return -1;
}
libtruc.c
#include <stdio.h>
#include <string.h>
 
        int
check_sc( char * foo, char * bar )
{
        if ( ! strncmp( foo, "TRUC", 4 ) )
        {       
                puts("   Il y a un truc !");
                return 0;
        }
        puts("   Il n'y avait pas de truc.");
        return -1;
}
compil.sh
#!/bin/sh

export LD_LIBRARY_PATH=".:"
rm dynlib libchose.so libtruc.so &>/dev/null
gcc -shared -nostartfiles -o libchose.so libchose.c
gcc -shared -nostartfiles -o libtruc.so libtruc.c
gcc -rdynamic -o dynlib main.c -ldl
./dynlib
Explications

C'est trop pénible d'expliquer : man dlopen (il y a un exemple et toutes les explications qu'il faut).

Le reste, si on connaît le C et ses pointeurs de fonctions, se comprend aisément.

jfg 2008/08/26 22:14

codaz/c/dlopen_dlsym_et_plugins_en_c.txt · Last modified: 2010/01/12 13:29 (external edit)