FYS : Free YourSelf library

Bibliothèque C++ généraliste dans le domaine public — Libère-toi, et je serai libre !

a marqué ce sujet comme résolu.

Voici quelques temps de cela, j'ai souhaité me lancer dans une projet à long terme, c'est maintenant chose faite :

La Free YourSelf library est une bibliothèque C++11 placée dans le domaine public. Elle à vocation à rester généraliste, tout en ne s'interdisant pas de contenir des systèmes avancés spécifiques à un domaine.

Elle contient actuellement un std::streambuf décodeur d'ogg/vorbis basée sur la stb_vorbis elle aussi dans le domaine public, ainsi que divers algorithmes que je compte proposer à l'ajout dans la Standard Library du C++.

Si vous avez des propositions, des remarques, des suggestions, ou toute sorte de contribution à offrir, je suis preneur :) .


Autant le dire tout de suite, ce n'est pas une réalisation dans laquelle je compte investir 100% de mon temps, cela serait d'ailleurs impossible car j'ai d'autres choses en cours. C'est plutôt quelque chose que je vais gérer sur le temps libre de mon temps libre.

Si je relègue sa gestion à mon temps libre, en revanche le code qu'elle contient ne l'est pas forcément : en effet, l'un des avantages du domaine public est que je puisse partager ce que je fais dans le cadre de mes autres projets — qui ne sont pas tous open source, il faut bien vendre pour vivre malheureusement — et également utiliser dans ces autres projets les avancées de FYS ce qui me permettra de les éprouver en “conditions réelles” et participer à la remontée de bugs, correction de ceux-ci, et des améliorations utiles. Ainsi il n'y a plus de perte de temps et de recodage de la roue carrée à cause des licences entre ce que je fais au travail et ce que je fais pour moi-même. J'espère aussi être utile à tous ceux qui trouveront dans FYS un bout de code qui fait ce dont ils ont besoin, et ce en toute circonstance sans angoisser sur des problèmes absurdes de copyright.

Tout cela part du principe que le travail coopératif est bien plus productif que le travail individuel. Mais cela part aussi d'une réflexion sur la nocivité du copyright. La liberté est pour moi un concept essentiel, et pour être libre de faire quelque chose, il faut être libre de le refuser. Pour partager, il faut y voir l'avantage mutuel, et non le faire parce que l'on est obligé de le faire, car ainsi on se préoccupe mieux de la qualité de la réalisation commune.

« La liberté commence là où commence celle des autres. »

Pierre Kropotkine

+0 -0

Yo,

J'ai été voir car je trouve ça intéressant et pourrait m’intéresser. Pour moi ton projet a UN gros défaut : Il n'y a aucune doc ! Et le code est très très peu documenté et commenté (a part stb_vorbis mais qui n'est pas de toi).

Je sais que c'est chiant, mais c'est la condition sine qua non pour que d'autres personne puissent utiliser ta lib et ensuite contribuent.

Pour moi dans l'ordre il faudrait :

  • Compléter le README : Le pavé de texte du README est assez long pour pas grand chose. Que tu souhaite donner ton point de vue sur le copyright que tu aime pas, soit. Mais perso quand j'arrive sur une page décrivant une lib je m’intéresse déjà à savoir ce qu'elle contient, plus que ça philosophie. Ce n'est pas une critique, juste un conseil : le pavé philosophique devrait être dans une section dédié. Mais avant tout il devrait avoir au minimum une description même sommaire de ce que contient la lib. Pour l'instant c'est encore jouable sans mais très vite c'est impossible. Donc un petit mot sur chaque fonctionnalité dispo serait la bienvenue.
  • Dans le header place au moins un commentaire avant chaque déclaration pour qu'on sache ce que fais la fonction, ce n'est pas toujours évident rien qu'au nom (find_nth) ni a quoi servent les paramètres (extract). Là on est obligé de ce plonger dans le code pour comprendre ce qui se passe. Dans un tel cas, je ne m'embeterai même pas à comprendre, je re-coderais ces fonctions car ça irait plus vite que de fouiller pour savoir si il y a une fonction qui fait bien ce que je veux.
  • Dans l'idéal met ces commentaire sous une forme normalisé pour générer automatiquement une doc (ex doxygen)
  • Quelques exemples, ou mieux, des tests unitaires et fonctionnelles seraient bienvenues.

Désolé si c'est un peu négatif. Je salue l'initiative mais il manque toute la partie chiante (pour le concepteur) et pourtant indispensable (pour l'utilisateur). Actuellement on ne peut, pour moi, pas l'utiliser et si j'étais arrivé dessus depuis ailleurs, je n'aurais pas dépassé la lecture du README.

Bon courage.

Merci de ce retour, je me penche sur cette question depuis hier, la doc est vraiment manquante. J'examine sous quel forme je pourrais la présenter, sachant que j'ai divers documents (pour les algos par exemple j'ai une présentation à la cppreference.com).

Pour le README, oui en effet, je vais y retravailler pour mettre la liste des features en premier plan.

Pour les exemples, je vais m'y atteler, il faut là aussi que je trouve un moyen uniformisé de les présenter.

Par ailleurs il faudrait que j'inclue tout ça dans le namespace fys:: parce que pour l'instant c'est encore très brouillon.

+0 -0

Yep, si déjà tu liste vite-fait dans le README un petit aperçu + une doc, c'est déjà bien. L'idéal étant peut être de mettre les exemples dans la doc.

Il y a pas mal d'outils pour générer de la doc a partir de commentaires dans le code, c'est souvent pas mal.

Voilà voilà, j'ai rajouté de la documentation complète et des exemples pour tous les algorithmes :) .

J'ai aussi fait le ménage dans le README et mis le tout dans un namespace fys::.

Chemin faisant j'ai également ajouté quelques variantes des algos.

Pour la documentation j'ai choisi un format qui certes n'est pas très conventionnel, mais qui je pense améliore sa qualité, et évite d'envahir le code. J'écrirai probablement un parseur un jour pour extraire une doc html en bonne et due forme, ainsi que pourquoi pas un plugin pour mon IDE permettant d'obtenir une aide contextuelle.

Je n'apprécie pas beaucoup la documentation à la doxygen, car je trouve qu'elle est inefficace pour le lecteur, et fichtrement ennuyeuse et redondante à rédiger côté développeur.

Et il m'a fallu une bonne semaine pour faire tout ça :D .

Par ailleurs j'ai buté (et malgré l'aide d'un ami mathématicien) sur le calcul de la complexité des incomplete_window, qui pour l'instant va rester sans réponse. Si le challenge intéresse quelqu'un, il s'agit de calculer le nombre de copies effectuées par incomplete_windows_copy.

Le problème se résume à simplifier l'algorithme suivant afin de faire disparaître la boucle dans le calcul :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int complexity_for(int length, int w_offset, int w_size)
{
    int count = 0;
    for(int i = 0; i < length; i += w_offset)
        count += min(w_size, length - i);
 
    return count;
}
 
int complexity_simple(int length, int w_offset, int w_size)
{
    return ???;
}

int main() {
    std::cout << complexity_for(18, 3, 7) << std::endl;
    std::cout << complexity_simple(18, 3, 7) << std::endl;
 
    return 0;
}

Ce calcul est important, car il permet de connaître notamment l'espace mémoire nécessaire pour recevoir les séquences copiées par l'algorithme.

Si vous avez des remarques ou des suggestions n'hésitez pas :) . Chemin faisant, un certain nombre d'algorithmes me sont venus à l'esprit, je vais m'employer à les coder.

+0 -0

Un petit ajout dernièrement : tuple_iterator un adaptateur d'itérateur qui permet de ne manipuler qu'un seul type lorsque l'on parcours une séquence de tuple/pair/… fort utile pour parcourir notamment seulement les clés ou seulement les valeurs d'une std::map :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int main()
{
    std::forward_list<std::tuple<int, std::string>> v{std::make_tuple(34, "coucou"),std::make_tuple(12, "wanna?")};
    std::unordered_map<int, std::string> m{std::make_pair(99, "hey!"),std::make_pair(22, "have a look!")};

    std::for_each(fys::make_tuple_iterator<0>(begin(v)), fys::make_tuple_iterator<0>(end(v)), [](int const& x){ std::cout << x << "\n"; });
    std::for_each(fys::make_tuple_iterator<1>(begin(m)), fys::make_tuple_iterator<1>(end(m)), [](std::string const& x){ std::cout << x << "\n"; });

    auto it = std::find(fys::make_tuple_iterator<0>(begin(v)), fys::make_tuple_iterator<0>(end(v)), 12).base();

    if(it != end(v))
    {
        std::cout << std::get<1>(*it) << "\n";
    }
}

Je suis à peu près sûr que boost permet ce genre de chose par ailleurs, mais comme je ne l'utilise pas, ce petit bout de code en domaine public me sera utile ^^. Encore que… une recherche google n'a rien donné pour boost.

Hésitez pas à piquer le code si besoin, c'est fait pour ça ;)

+0 -0

Pour la documentation, penche-toi sur Doxygen. C'est le truc à avoir pour faire de la doc' sans se casser la tête.

Richou D. Degenne

Mmh, je pars avec un à priori vraiment très très en défaveur de doxygen (je ne veux vraiment pas mettre ma doc au milieu du code). Mais il faut que je prenne le temps d'étudier la bête et voir si y'a moyen d'en tirer ce dont j'ai besoin.

+0 -0

Avec Doxygen, ta doc est écrite avec tes déclarations, pas dans le code. L'intérêt, c'est qu'on peut se documenter sans avoir à se pencher sur l'implémentation. Et, si t'es vraiment réticent à lire un fichier .h(pp), on peut toujours lire le HTML ou le PDF produit par Doxygen.

En plus, le formalisme introduit par Doxygen oblige tes commentaires à être structurés et bien présentés, chose que l'on n'a pas tendance à faire quand on commente soi-même.

Après un long moment sans ajout, en voici un qui m'a énormément servi et me sert encore dans mon dernier projet, il s'agit de underlying_cast : il permet de récupérer la valeur entière d'un enum class.

Avec son petit frère underlying_reference_cast qui lui permet de manipuler une enum class de la même façon qu'une enum old scool (parfois c'est utile).

J'étais fatigué de faire des appels partout à std::underlying_type<decltype(e)>::type, et l'usage est complètement transparent :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int main()
{
    enum class E {A,B,C};

    E e = E::B;
    std::cout << fys::underlying_cast(e) << std::endl;

    fys::underlying_reference_cast(e) = 2;
    std::cout << fys::underlying_cast(e) << std::endl;
}

Lien vers le header : utility/underlying_cast.h

+0 -0

Un beau déterrage pour un petit ajout sympathique grâce à @jo_link_noir qui m’a permis de simplifier le code sur lequel je travaillais : fys::is

Cela permet de compléter le std::is_same<T,U> qui ne s’utilise que sur des types, et non des variables. Cela nous épargne un std::decay et un decltype qui alourdissent le code, ou une macro si comme moi on aime avoir du code lisible.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int main()
{
    std::vector<std::variant<int, bool>> v = {3, true, 40};
    for(auto const& var : v){
        std::visit([](auto& x){
            if constexpr(fys::is<int>(x)){
                std::cout << "integer " << x << std::endl;
            } else if constexpr(fys::is<bool>(x)){
                std::cout << "boolean " << x << std::endl;
            }
        }, var);
    }
    //Without fys::is, this code would require in the general case :
    // if constexpr(std::is_same_v<std::decay_t<decltype(x)>, int>)
}
1
2
3
integer 3
boolean true
integer 40

Lien vers le header : utility/is.h

+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte