• # L'intérêt? Éviter de refaire l'encapsulation, mais...

    Posté par . En réponse au message Wrapper. Évalué à 3.

    Ma question est donc quel intérêt (au delà de la syntaxe) ai-je à favoriser le binding C++ plutôt qu'utiliser directement cairo ?

    Simple: quand on utilise une lib C, il faut se fader l'encapsulation. Alors, quand ce sont des pointeurs, ça va encore, un coup de

    //code filé par la lib
    //{
    struct my_damn_struct
    {
    };
    my_damn_struct* make_my_damn_struct( ... )
    {
     return malloc my_damn_struct;
    }
    void clean_my_damn_struct( my_damn_struct* p )
    {
     free( p );
    }
    //}
    //code que tu dois te fader
    typedef std::unique_ptr<my_damn_struct,void (*)(my_damn_struct*)> my_damn_struct_ptr;
    my_damn_struct_ptr make_my_damn_struct_ptr( ... )
    {
     return my_damn_struct_ptr( make_my_damn_struct( ... ), clean_my_damn_struct );
    }

    et te résigner à ne plus utiliser que make_my_damn_struct_ptr pour initialiser tes données et la méthode get() pour y accéder. Bien sûr, aucun outil pour le garantir. Et, bien sûr, si tu dois instancier beaucoup de pointeurs, tu vas avoir un callback en rab à chaque fois, ce qui va fatalement consommer de la mémoire (mais j'imagine que la variante tableaux de unique_ptr permets d'éviter ça... tant qu'on n'a pas besoin de vector ou de list...).
    Mais, ça va encore.
    Tu as aussi l'alternative de faire une classe dédiée à chaque structure, mais c'est long, chiant, avec un haut risque d'erreur (comme tout code long et chiant en fait).

    Par contre, si la bibliothèque n'utilise PAS des pointeurs (comme OpenGL ou les sockets BSD), la, tu l'as dans le... tu es obligé de faire des classes spécialisées: std::unique_ptr ne sait gérer que de la mémoire (et avec une interface bien lourde à utiliser en code, sans parler du fait d'avoir des callbacks dans chaque instance de pointeur...)
    Personnellement, je me suis bricolé un template unique_res pour ça, qui me sert au final aussi pour les pointeurs.

    Donc, le voila, l'intérêt d'utiliser des bindings: laisser le boulot chiant aux autres.
    Mais, parce que tu auras noté le "mais..." de mon titre, ça veut aussi dire que tu te lies à une bibliothèque précise, de qualité variable, un peu partout dans ton code, que tu ne contrôles que peu ou prou.
    Moi, je me suis aperçu qu'au final je finis toujours par encapsuler ce que je récupère de l'«étranger», alors je préfère utiliser les sources C. Les fois ou je me suis laissé aller à utiliser (par flemme) les bindings C++, j'ai finit par le regretter rapidement, pour plusieurs raisons:

    • API pas forcément bien faite (libpqxx est ma dernière expérience à ce sujet) par exemple au niveau des exceptions (d'ailleurs, il semble que celles-ci n'aient pas d'ABI standard, ce qui signifie que se lier à une DLL/so qui lance des exceptions c'est dangereux).
    • API bugguées (j'ai eu une mauvaise expérience avec opencv, par le passé, ou j'étais du coup repartit sur l'API C parce que celle en C++ semblait bugguer. Mais ça date.)
    • API mal documentées (pas d'exemple pour le coup, mais je me souviens l'avoir vécu...)

    Alors que souvent, les libs C, utilisées par plus de monde, souffrent moins (pas: "ne souffrent pas", mais "souffrent moins", j'insiste) de ces problèmes.
    Du coup, je préfère utiliser directement les libs C et les encapsuler moi-même. De toute façon, même les objets C++ que j'importe je les encapsule, pour limiter l'impact d'un changement de lib à un nombre de fichiers restreints dans mon code.
    Comme ça, le cœur est entièrement «maison» et fait appel à des interface qui elles interagissent avec les choses sur lesquelles je n'ai pas le contrôle direct.
    Par contre, mon approche à un inconvénient de taille: plus de lignes de code, maintenance «plus chère» tant que les interfaces bougent (mais une fois stable, c'est le contraire).