Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#101 Le 17/03/2017, à 16:58

grim7reaper

Re : /* Topic des codeurs [9] */

@Dafyd: Yep, Haskell c’est bien sympa comme langage fonctionnel. Tu peux partir là-dessus si tu veux smile

Hors ligne

#102 Le 29/06/2017, à 21:10

Elzen

Re : /* Topic des codeurs [9] */

Bon, je suis en train de tenter de migrer Touhy à GTK3 (pour enfin pouvoir faire du Python3), mais il y a un paquet de trucs qui ne s'annoncent pas évidents hmm

Par exemple, pour faire ma barre d'onglets en haut de l'écran. Évidemment, un widget qui présente seulement cette barre d'onglets, indépendante du Notebook, ça n'existe toujours pas (il me semble que ça existe en Qt). Donc, comme pour la version GTK2, je tente de « tricher » en mettant un Notebook et en en cachant la partie du bas. Sauf que j'ai eu beau essayer tous les moyens de régler la taille de la fenêtre que j'ai pu trouver, pas moyen de réduire la fenêtre à la bonne hauteur. Même en mettant le Notebook dans un Viewport, qui est censé être fait pour ça, ça continue d'agrandir la fenêtre pour me l'afficher en entier yikes
Donc, j'ai tenté de me rabattre sur l'autre option: laisser la fenêtre prendre trop de place, et utiliser du shape (l'extension de X11 pour faire des trous dans les fenêtres même sans composite) pour n'afficher que la partie utile. Sauf que, là encore: la fonction shape_combine_mask de GTK2, qui prenait un Pixmap facile à obtenir comme argument, a été remplacée par shape_combine_region, qui prend désormais un objet conçu sur mesure. Pour générer cet objet région, deux fonctions existent en GDK : create_from_pixbuf qui n'a pas l'air disponible en Python, et create_from_surface… qui cause une segfault.
J'ai tenté de créer l'objet Region en attaquant cairo directement, mais sans succès pour le moment. Le seul espoir que je vois de réussir à faire ça, là, c'est d'attaquer la Xlib directement, si seulement c'est faisable…

Franchement, si quelqu'un a un tuyau, je prends hmm

Hors ligne

#103 Le 03/07/2017, à 14:15

grim7reaper

Re : /* Topic des codeurs [9] */

Si je comprends bien ce que tu veux faire, tu veux voir seulement [page 1][page 2][page 3] sur cet exemple?
Est-ce que tu dois vraiment passer par un Notebook?
Je connais pas les détails mais tu pourrais pas simplement avoir une fenêtre avec des boutons/whatever empilé horizontalement?

Hors ligne

#104 Le 03/07/2017, à 22:18

Elzen

Re : /* Topic des codeurs [9] */

Bah, techniquement, implémenter ça avec des boutons ou n'importe quel autre truc cliquable simple d'utilisation serait beaucoup plus facile, oui; mais le principe du truc est de simuler une barre d'onglets pour gérer le multibureaux (un onglet = un espace de travail). Or, à moins de trouver comment intégrer une feuille de style spécifique pour ce truc-là (auquel cas ça aura toutes les chances de ne pas coller avec le thème sur une autre machine que la mienne), faire autrement ne rendrait pas du tout la tronche des onglets et donc casserait un peu le truc.

Pour vous donner une idée, la version GTK2 ressemble à ça:
1499116623.png

(Après vérification, il y a bien une barre d'onglets standalone dans Qt, un truc comme ça en GTK me simplifierait considérablement la vie)

Hors ligne

#105 Le 06/07/2017, à 07:12

grim7reaper

Re : /* Topic des codeurs [9] */

Hum, en effet mis à part le Notebook je ne vois pas comment faire ça en GTK hmm, c’est dommage de pas avoir un widget pour ça.
Au lieu de migrer GTK 2 -> GTK 3, mieux vaut peut-être migrer GTK 2 -> Qt ^^

Hors ligne

#106 Le 06/07/2017, à 10:41

Elzen

Re : /* Topic des codeurs [9] */

C'est ce que je comptais faire à une époque, mais le pas à franchir entre les deux m'a pas mal refroidi (je ne sais plus les détails, mais il me semble que Qt a une logique inversée par rapport à tout ce que je connais d'autre comme bibliothèques graphiques, du genre passer les conteneurs aux contenus et pas l'inverse, par exemple).
Puis, en regardant dans les détails, GTK3 apporte quelques trucs assez sympa et agréables à utiliser, et y a quelques améliorations logiques au niveau du code (même s'il y a aussi pas mal de trucs sympas qui disparaissent et que le pseudo-CSS pour le style est juste une énorme régression), donc maintenant que je commence à me débrouiller avec la bête, j'aurais bien aimer continuer…

En GTK2, j'avais réussi à me faire un widget «barre d'onglet» sans trop de difficultés en mettant un Notebook dans un Viewport, et ça pouvait marcher dans toutes les directions facilement; mais là, vu que le viewport n'est pas foutu de viewporter, ça va être plus dur. Si j'arrivais juste à faire marcher le shape, ça pourrait aller pour cet outil-là, au moins, mais dans l'immédiat, à part taper dans la Xlib, ce dont j'ai moyennement envie, je ne vois pas.

(Un jour, j'me coderai ma propre bibliothèque graphique, ce sera plus simple ><)

Hors ligne

#107 Le 06/07/2017, à 22:34

HP

Re : /* Topic des codeurs [9] */

The Safe Navigation Operator (&.) in Ruby

http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

if account && account.owner && account.owner.address

end
account&.owner&.address

Petite trouvaille du soir… en utilisant l'excellent RuboCop qui m'a gratifié du message suivant :

lib/ylem/helper/subprocess.rb:44:33: C: Style/SafeNavigation: Use safe navigation (&.) instead of checking if an object exists before calling the method.
        .map { |source, thread| thread.join if thread }

cat /dev/urandom >/dev/null 2>&1 #github

Hors ligne

#108 Le 07/07/2017, à 11:06

cervo

Re : /* Topic des codeurs [9] */

HP a écrit :

The Safe Navigation Operator (&.) in Ruby

http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

à utiliser sans modération

Hors ligne

#109 Le 10/07/2017, à 14:22

Elzen

Re : /* Topic des codeurs [9] */

Tiens, en attendant des pistes de résolutions du problème sus-mentionné, un autre truc qui me trotte dans la tête.

Sur mon blog, actuellement, il y a une version http pas sécurisé accessible à tout le monde, et une version https qui demande un login/mdp (m'enfin, mettre guest:guest, ça passe). Je fais ça par une authentification http directement, la plus grande partie du site étant statique et rajouter une couche de cgi juste pour ça me plaisant moyen.
J'aimerais bien mettre en place un accès https qui ne nécessite pas d'entrer de login/mdp, histoire que n'importe qui puisse naviguer sur mon site de façon chiffrée sans se faire prendre la tête par une popup dès l'ouverture (même que ça pourrait être une raison qui me motiverait à passer à let's encrypt plutôt que de rester autosigné). Pour autant, il y a des trucs pour lesquels je veux garder un accès restreint, et perso, j'aime bien que le mot de passe soit demandé une fois pour toute à l'affichage de la première page, même si elle n'a rien qui nécessite une sécurisation, plutôt que de tomber dessus comme ça en cours de navigation.

Du coup, je me demande: est-il, à votre connaissance, possible de configurer le serveur web pour que:
– Si tu insistes pour te loguer (genre en précisant login@ dans l'URL de la première page que tu affiches), tu es logué une bonne fois pour toutes, et les informations resteront transmises pendant toute ta navigation,
– Si non, ça te laisse juste naviguer normalement, comme s'il n'y avait pas d'authentification?

Edit: configurer le serveur web, attendu que je suppose que c'est le serveur qui redemande au navigateur les infos de login à chaque tentative de connexion, et pas le navigateur qui les envoie tout seul, dans le doute, en continuant même si ça ne sert à rien… mais je n'ai jamais testé, en fait yikes Si quelqu'un a de la bonne doc' sur la façon dont est gérée l'authentification http des deux côté, ça m'intéresse.

Dernière modification par Elzen (Le 10/07/2017, à 14:31)

Hors ligne

#110 Le 12/07/2017, à 12:33

grim7reaper

Re : /* Topic des codeurs [9] */

Elzen a écrit :

Si quelqu'un a de la bonne doc' sur la façon dont est gérée l'authentification http des deux côté, ça m'intéresse.

À priori, ce que tu utilises c’est le Digest access authentication de HTTP, du coup Wikipédia est un bon point de départ.

L’outil Network de Firefox (dans la section Developer  chez moi) permet aussi de se faire une idée de ce qui se passe en regardant les requêtes HTTP qui passent.
De ce que j’ai vu après un rapide test:
- accès en HTTP: RAS.
- accès en HTTPS:
* Le navigateur faire un GET
* Ton serveur répond 401 avec l’en-tête WWW-Authenticate:"Digest realm="", nonce="4b179c56e2ae416d86acf85bbe2b691d", qop="auth""
* Mon navigateur renvoie la requête GET avec l’en tête Authenticate qui va bien
* Ton serveur répond 200
* Je clique sur un lien pour aller sur une autre page (toujours en HTTPS) => mon navigateur renvoie l’en tête Authenticate (sans me redemander mon login/mot de passe, il doit guarder ça en cache…)
* Ton serveur me répond 200 (vu que je continue d’envoyer l’en-tête)

Si j’édite la requête HTTP pour enlever l’en-tête ou vide le cache qui contient mes identifiants, ton serveur va me rebalancer un 401 pour me demander de me loguer (parce que je n'envoie plus l’en-tête).
Donc à priori, c'est renvoyé pour chaque page (ce qui ne semble pas surprenant au final : HTTP étant un protocole stateless il ne peux pas se souvenir si tu es loggué ou pas).

Dernière modification par grim7reaper (Le 12/07/2017, à 12:35)

Hors ligne

#111 Le 12/07/2017, à 13:05

Elzen

Re : /* Topic des codeurs [9] */

Ça n'a rien de surprenant, en effet; mais je pensais naïvement que le navigateur attendait de recevoir la demande d'authentification de la part du serveur pour renvoyer les infos (ce qui n'aurait pas nécessité de session non plus, le serveur envoyant la demande systématiquement tant qu'il ne reçoit pas ce dont il a besoin).

Du coup, la question serait de tenter de mettre l'en-tête qui va bien sans pour autant renvoyer la 401, et de regarder ce qui se passe dans ce cas-là; mais la problématique a l'air de se situer principalement du côté du navigateur.

Merci pour le lien, je vais détailler ça.


(Sinon, pour l'autre problème, j'ai trouvé une parade temporaire, faute de mieux: une GtkSocket dans le panel en GTK3, et la barre d'onglets dans un GtkPlug géré par du code PyGTK. Pas forcément ce qu'il y a de plus léger, mais d'un autre côté, ça permet une certaine souplesse)

Hors ligne

#112 Le 13/07/2017, à 09:57

grim7reaper

Re : /* Topic des codeurs [9] */

Elzen a écrit :

Ça n'a rien de surprenant, en effet; mais je pensais naïvement que le navigateur attendait de recevoir la demande d'authentification de la part du serveur pour renvoyer les infos (ce qui n'aurait pas nécessité de session non plus, le serveur envoyant la demande systématiquement tant qu'il ne reçoit pas ce dont il a besoin).

En effet, ça aurait été une solution possible mais du coup ça demande 2 requêtes par page (un GET qui se prend un 401, puis un GET avec les informations), c’est peut-être pour ça que ça n’a pas été retenu.

Elzen a écrit :

(Sinon, pour l'autre problème, j'ai trouvé une parade temporaire, faute de mieux: une GtkSocket dans le panel en GTK3, et la barre d'onglets dans un GtkPlug géré par du code PyGTK. Pas forcément ce qu'il y a de plus léger, mais d'un autre côté, ça permet une certaine souplesse)

Tu peux détailler?
Je connaissais pas ce système de GtkSocket/GtkPlug.

Hors ligne

#113 Le 13/07/2017, à 12:47

Elzen

Re : /* Topic des codeurs [9] */

grim7reaper a écrit :

En effet, ça aurait été une solution possible mais du coup ça demande 2 requêtes par page (un GET qui se prend un 401, puis un GET avec les informations), c’est peut-être pour ça que ça n’a pas été retenu.

Yep, en effet.

Mais du coup, j'imagine que pour des sites pour lesquels la demande d'authentification ne concerne qu'une petite partie de l'arborescence, le navigateur continue d'envoyer les infos de connexion à chaque fois alors que c'est inutile, ce qui n'est pas forcément glop non plus. Il faudra que je regarde ce que le serveur en fait dans ces cas-là. J'me demande aussi comment le navigateur réagit quand il se mange une 401 sur une requête AJAX, demande-t-il l'authentification comme ça, alors que le reste de la page est déjà chargée? Bref, quand je serai en situation de pouvoir bosser correctement, je ferai quelques tests et j'aviserai en fonction.

grim7reaper a écrit :

Tu peux détailler?
Je connaissais pas ce système de GtkSocket/GtkPlug.

Dans l'usage de base, c'est relativement simple; après, naturellement, je m'en sers bizarrement big_smile

Un gtk.Plug est un widget top level, comme une fenêtre, mais qui n'est pas conçu pour être affiché directement. Il doit être embarqué dans un autre (ça passe par un protocole X dédié, Xembed). Le gtk.Socket est le widget conteneur associé. Un des objectifs est de permettre de gérer une partie du contenu d'une fenêtre depuis un autre processus, et ça peut aussi servir à croiser les bibliothèques graphiques. Je subodore que ce soit ce qui est utilisé pour les dockapps de Window Maker, par exemple; ou pour la prévisualisation des thèmes GTK dans lxappearance.

Dans mon cas, je configure mon panel (GTK3) en indiquant les noms des éléments à utiliser, et si un des noms proposés ne correspond à rien, je place une Socket, et je publie sur un presse-papier dédié (j'aime bien utiliser des presses-papiers dédiés, c'est un des moyens de communication entre applis les plus simples à utiliser, à mon sens) le nom du widget et l'Xid de la Socket. Comme ça, mon appli en GTK2 n'a plus qu'à consulter le presse-papier en question, remarquer qu'on lui demande la barre de menu, et la créer dans le Plug qui ira s'insérer dans la Socket en question. Ça permettra aussi d'enrichir le panel depuis d'autres applis en cas de besoin.

Hors ligne

#114 Le 13/07/2017, à 13:53

grim7reaper

Re : /* Topic des codeurs [9] */

Merci pour les détails.

Hors ligne

#115 Le 14/08/2017, à 02:10

Elzen

Re : /* Topic des codeurs [9] */

grim7reaper a écrit :

Si je comprends bien ce que tu veux faire, tu veux voir seulement [page 1][page 2][page 3] sur cet exemple?
Est-ce que tu dois vraiment passer par un Notebook?
Je connais pas les détails mais tu pourrais pas simplement avoir une fenêtre avec des boutons/whatever empilé horizontalement?

J'ai réussi \o/

Il a fallu pas mal de temps pour trouver, mais j'ai enfin réussi à tracer des onglets tous seuls, sans le gtk.Notebook autour.

Il « suffit » d'utiliser les méthodes gtk_render_* avec l'objet StyleContext qui va bien pour le widget désiré (en l'occurrence, il faut utiliser gtk_render_background et gtk_render_frame. Quoique les onglets soient explicitement mentionnés dans la description de gtk_render_extension, en fait, cette méthode fait la même chose que les précédentes, mais en virant la bordure sur un côté, ce qui est pratique dans certains cas mais ne sert pas ici).
Le souci, c'est que j'ai eu beau essayer plein de manières différentes d'obtenir un StyleContext à partir d'un Widget GTK (méthode get_style_context(), en essayant de recréer l'arborescence sur les objets), ce qui se dessinait ne ressemblait jamais à un onglet. Et en fait, je viens de réussir ce soir en créant le StyleContext entièrement à la main pour y mettre les informations que je veux.

Du coup, ça donne un truc comme ça:

style = gtk.StyleContext()
if active:  # Styler comme l'onglet actif.
    style.set_state(gtk.StateFlags.CHECKED)
wpath = gtk.WidgetPath()
for i, name in enumerate(["notebook", "header", "tabs", "tab"]):
    wpath.append_type(gtk.Notebook)  # Sans un append_ avant le iter_set_, ça segfault.
    wpath.iter_set_object_name(i, name)
wpath.iter_add_class(1, edge)  # "top", "left", "right" ou "bottom"
style.set_path(wpath)

Et ensuite, on peut tranquillement peindre ça, et ça a bien la tête demandée.

Il suffit donc ensuite de peindre ça sur le fond des différents widgets censés ressembler à des onglets, et tout marche.
Il faut encore que je joue un peu avec pour être sûr, mais je crois bien que ce truc est réglé smile


(Sinon, pour le shape, en fait c'est en fait très simple à faire, maintenant :

window.shape_combine_region(cairo.Region([cairo.RectangleInt(x1, y1, w1, h1), cairo.RectangleInt(x2, y2, w2, h2), …])

mais bon, du coup je n'en ai plus besoin pour ça, donc ça va.)


Par contre, évidemment, ce n'est pas comme si j'avais tout réglé non plus.
Par exemple, pour gérer le bureau, j'aimerais bien pouvoir afficher l'image d'arrière plan X simplement. En GTK2, ça se faisait comme ça:

wid = wnck.screen_get_default().get_background_pixmap()
if wid > 0L:
    display = gtk.gdk.display_get_default()
    pixmap = gtk.gdk.pixmap_foreign_new_for_display(display, wid)
    pixmap.set_colormap(gtk.gdk.screen_get_default().get_rgb_colormap())
    # S'il y a besoin de convertir le pixmap en pixbuf…
    w, h = pixmap.get_size()
    pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, w, h)
    pixbuf.get_from_drawable(pixmap, gtk.gdk.colormap_get_system(), 0, 0, 0, 0, w, h)

La fonction get_background_pixmap() existe encore dans WNCK, donc j'arrive encore à accéder à l'id de la fenêtre X correspondante, mais c'est ensuite que ça se gâte :
– gdk.pixmap_foreign_new_for_display n'existe plus en GTK3, puisque les GdkPixmap ont été entièrement dégagés au profit des CairoSurface (ce qui n'est pas forcément plus mal, d'ailleurs).
– Il existe normalement une fonction cairo_xlib_surface_create_for_bitmap qui devrait avoir le même effet, mais après recherche, elle n'a présentement pas de binding python (apparemment, parce qu'ils ne connaissent pas python-xlib).
– Il y a bien un GdkX11.X11Window.foreign_new_for_display, mais il ne marche pas pour récupérer ça, apparemment (ça permet par contre d'accéder aux vraies fenêtres, il y avait une fonction équivalente en GTK2 aussi).

Du coup, en attendant, je lance une appli en PyGTK en arrière-plan qui récupère cette image et me la colle dans un presse-papier, histoire de pouvoir ensuite la récupérer dans GTK3 quand même, ce qui n'est pas franchement la solution idéale.
Si quelqu'un a une piste pour ce dernier problème, je prends smile

Hors ligne

#116 Le 18/08/2017, à 17:56

Elzen

Re : /* Topic des codeurs [9] */

Bon, double post, parce que :

Elzen a écrit :

J'ai réussi \o/

[…]

Il suffit donc ensuite de peindre ça sur le fond des différents widgets censés ressembler à des onglets, et tout marche.
Il faut encore que je joue un peu avec pour être sûr, mais je crois bien que ce truc est réglé smile

Eut-ce été si simple que ça que ça n'aurait pas été drôle roll

Le moyen décrit ci-dessus marche très bien pour tracer un onglet, prit isolément de tout le reste. Le truc est que les onglets sont rarement isolés, et que parfois, les thèmes ont des petites subtilités quand ils se suivent. Du genre, virer la bordure de gauche des onglets qui ne sont pas en première position pour éviter que ça fasse trop large. Donc il y a des trucs à rajouter dans mon code.

La section « description » du GtkWidgetPath dans la doc parle encore d'utiliser des GtkRegionFlags pour ce genre de trucs, mais ils sont dépréciés depuis un bout de temps (et n'ont pas donné grand chose quand j'ai testé). Donc je suppose qu'il faut plutôt passer par append_with_siblings, mais je ne pige rien à la façon de s'en servir.

Tenez, plutôt que de longues explications, je pense qu'une simple capture sera assez claire sur le problème :
1503075122.png
(Notez que, ça n'apparaît pas sur la légende, mais dans la CSS utilisée pour cet exemple, j'ai aussi le fait que le sélecteur « tab+tab » double la largeur de la bordure, ce qui ne marche nulle part ici).

Si quelqu'un capte quelque chose à la façon d'utiliser append_with_siblings, tout coup de main sera le bienvenu (jusque là, je n'ai trouvé d'exemple d'utilisation nulle part, seulement divers clones de la page de doc' sus-liée qui n'est pas très détaillée à ce sujet)

Ceci dit, au passage, vous pouvez quand même remarquer, en haut de l'écran, ma barre d'onglets des bureaux version GTK3 in situ, en lieu et place de celle en GTK2 sur la capture précédente smile

Hors ligne

#117 Le 11/01/2018, à 07:04

Dafyd

Re : /* Topic des codeurs [9] */

Bonjour amis codeurs. Ca fait un moment que je ne suis pas venu ici...
J'aurais besoin d'un coup de main pour traquer une fuite de mémoire, je ne comprends pas... Voici le code :

list.h

#ifndef HEADER_LIST
#define HEADER_LIST


#include <stdlib.h>


typedef struct list_priv_t* list_priv_t;
typedef struct list_t* list_t;


struct list_t {
    list_priv_t priv;

    size_t (*length)(list_t list);
    void (*add)(list_t list, void *el);
    void* (*get)(list_t list, size_t index);
    void (*remove_at)(list_t list, size_t index);
    void (*remove)(list_t list, void *el);
};


list_t list_new(void);
void list_del(list_t *list);
void list_del_with_free(list_t *list);


#endif

list.c

#include "utils/list.h"


struct list_priv_t {
    size_t length;
    void ** objs;
};


static void rearrange(list_t list) {
    size_t i, j;

    for (i = 0; i < list->priv->length; i++) {
        if (list->priv->objs[i] == NULL) {
            for (j = i; j < list->priv->length - 1; j++) {
                list->priv->objs[j] = list->priv->objs[j + 1];
            }
            list->priv->length--;
            list->priv->objs = realloc(list->priv->objs, list->priv->length * sizeof(*(list->priv->objs)));
            i--;
        }
    }
}

static size_t length_impl(list_t list) {
    return list->priv->length;
}

static void add_impl(list_t list, void *el) {
    list->priv->objs = realloc(list->priv->objs, (list->priv->length + 1) * sizeof(el));
    list->priv->objs[list->priv->length] = el;
    list->priv->length++;
}

static void* get_impl(list_t list, size_t index) {
    if (index >= list->priv->length) {
        return NULL;
    }
    else {
        return list->priv->objs[index];
    }
}

static void remove_at_impl(list_t list, size_t index) {
    if (index < list->priv->length) {
        list->priv->objs[index] = NULL;
        rearrange(list);
    }
}

static void remove_impl(list_t list, void *el) {
    size_t i;

    for (i = 0; i < list->priv->length; i++) {
        if (list->priv->objs[i] == el) {
            list->priv->objs[i] = NULL;
            rearrange(list);
        }
    }
}

list_t list_new(void) {
    list_t list = malloc(sizeof(*list));
    list->priv = malloc(sizeof(*(list->priv)));
    list->priv->length = 0;
    list->priv->objs = NULL;

    list->length = &length_impl;
    list->add = &add_impl;
    list->get = &get_impl;
    list->remove_at = &remove_at_impl;
    list->remove = &remove_impl;

    return list;
}

void list_del(list_t *list) {
    if (list && *list) {
        if ((*list)->priv) {
            if ((*list)->priv->objs) {
                free((*list)->priv->objs);
            }
            free((*list)->priv);
        }
        free(*list);
        *list = NULL;
    }
}

void list_del_with_free(list_t *list) {
    size_t i;

    for (i = 0; i < (*list)->length(*list); i++) {
        free((*list)->get((*list), i));
    }

    list_del(list);
}

str.h

#ifndef HEADER_STR
#define HEADER_STR


#include <stdlib.h>
#include <string.h>

#include "utils/list.h"


typedef struct str_priv_t* str_priv_t;
typedef struct str_t* str_t;


struct str_t {
    str_priv_t priv;

    char* (*cstr)(str_t str);
    void (*set)(str_t str, char* src);
    list_t (*split)(str_t str, str_t delimiter);
};


str_t str_new(void);
str_t str_new_from(char* cstr);
void str_del(str_t *str);


#endif

str.c

#include "utils/str.h"


struct str_priv_t {
    char* str;
};


static char* cstr_impl(str_t str) {
    return str->priv->str;
}

static void set_impl(str_t str, char* src) {
    str->priv->str = realloc(str->priv->str, strlen(src) + 1);
    strcpy(str->priv->str, src);
}

static list_t split_impl(str_t str, str_t delimiter) {
    char *begin, *end, *buf = NULL;
    size_t length;
    list_t list = list_new();
    
    begin = str->cstr(str);
    while ((end = strstr(begin, delimiter->cstr(delimiter)))) {
        length = end - begin;

        buf = calloc(length + 1, 1);
        strncpy(buf, begin, length);
        
        list->add(list, (void*)str_new_from(buf));

        free(buf);

        begin = end + 1;
    }

    list->add(list, (void*)str_new_from(begin));

    return list;
}

str_t str_new(void) {
    str_t str = malloc(sizeof(*str));
    str->priv = malloc(sizeof(*(str->priv)));
    str->priv->str = malloc(1);
    strcpy(str->priv->str, "");

    str->cstr = &cstr_impl;
    str->set = &set_impl;
    str->split = &split_impl;

    return str;
}

str_t str_new_from(char* cstr) {
    str_t str = str_new();
    str->set(str, cstr);

    return str;
}

void str_del(str_t *str) {
    if (str && *str) {
        if ((*str)->priv) {
            if ((*str)->priv->str) {
                free((*str)->priv->str);
            }
            free((*str)->priv);
        }
        free(*str);
        *str = NULL;
    }
}

Et le petit main qui utilise tout cela pour test:

#include <stdlib.h>
#include <stdio.h>

#include "utils/str.h"


int main(void) {
    size_t i;
    str_t str, delimiter;
    list_t list;

    str = str_new();
    delimiter = str_new();

    str->set(str, "toto:tutu:lol");
    delimiter->set(delimiter, ":");

    list = str->split(str, delimiter);

    printf("[");
    for (i = 0; i < list->length(list); i++) {
        void *el = list->get(list, i);
        if (el) {
            printf("%s", ((str_t)el)->cstr((str_t)el));
        }
        else {
            printf("NULL");
        }
        if (i < list->length(list) - 1) {
                printf(", ");
        }
    }
    puts("]");

    str_del(&str);
    str_del(&delimiter);
    list_del_with_free(&list);
    
    return EXIT_SUCCESS;
}

Valgrind me sort ceci :

==4338== Memcheck, a memory error detector
==4338== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4338== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==4338== Command: ./jeudemerdequiserajamaisfinicommedhab
==4338==
[toto, tutu, lol]
==4338==
==4338== HEAP SUMMARY:
==4338==     in use at exit: 38 bytes in 6 blocks
==4338==   total heap usage: 28 allocs, 22 frees, 1,381 bytes allocated
==4338==
==4338== 12 (8 direct, 4 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 4
==4338==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4338==    by 0x1093CA: str_new (str.c:44)
==4338==    by 0x10943E: str_new_from (str.c:56)
==4338==    by 0x10938F: split_impl (str.c:37)
==4338==    by 0x108CEA: main (main.c:18)
==4338==
==4338== 26 (16 direct, 10 indirect) bytes in 2 blocks are definitely lost in loss record 4 of 4
==4338==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4338==    by 0x1093CA: str_new (str.c:44)
==4338==    by 0x10943E: str_new_from (str.c:56)
==4338==    by 0x109322: split_impl (str.c:30)
==4338==    by 0x108CEA: main (main.c:18)
==4338==
==4338== LEAK SUMMARY:
==4338==    definitely lost: 24 bytes in 3 blocks
==4338==    indirectly lost: 14 bytes in 3 blocks
==4338==      possibly lost: 0 bytes in 0 blocks
==4338==    still reachable: 0 bytes in 0 blocks
==4338==         suppressed: 0 bytes in 0 blocks
==4338==
==4338== For counts of detected and suppressed errors, rerun with: -v
==4338== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

Donc apparemment mon list_del_with_free ne fait pas le boulot correctement, mais je ne comprends pas pourquoi...

Voila, si quelqu'un a une idée, je suis preneur !! tongue

Edit : Je suis au courant que mes realloc sont crades : https://grim7reaper.rolinh.ch/blog/2013 … e-realloc/
Ça fait partie des trucs à nettoyer dans ce code. Disons que la, j'en suis à la phase "architecturale" du programme. J'ai pas essayé de faire un truc super propre, mais ça viendra.

Dernière modification par Dafyd (Le 11/01/2018, à 10:18)

Hors ligne

#118 Le 15/01/2018, à 13:33

grim7reaper

Re : /* Topic des codeurs [9] */

Salut Dafyd,

Ça ne viendrait pas du fait que ton list_del_with_free appelle seulement free sur les éléments, alors qu’il devrait appeler str_del pour bien tout libérer (car tes éléments sont de type str_t donc un simple free ne va pas suffire)?

Je ne ferais pas de commentaire sur le reste du code vu que tu est encore en mode "test" tongue

Édit: je confirme, mon Valgrind me donne cette trace

==9447== 4 bytes in 1 blocks are indirectly lost in loss record 1 of 4
==9447==    at 0x4C2F0FF: realloc (vg_replace_malloc.c:785)
==9447==    by 0x108F7F: set_impl (str.c:14)
==9447==    by 0x10916A: str_new_from (str.c:57)
==9447==    by 0x10909F: split_impl (str.c:37)
==9447==    by 0x108E21: main (main.c:18)
==9447==
==9447== 10 bytes in 2 blocks are indirectly lost in loss record 2 of 4
==9447==    at 0x4C2F0FF: realloc (vg_replace_malloc.c:785)
==9447==    by 0x108F7F: set_impl (str.c:14)
==9447==    by 0x10916A: str_new_from (str.c:57)
==9447==    by 0x109032: split_impl (str.c:30)
==9447==    by 0x108E21: main (main.c:18)
==9447==
==9447== 12 (8 direct, 4 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 4
==9447==    at 0x4C2CEDF: malloc (vg_replace_malloc.c:299)
==9447==    by 0x1090DA: str_new (str.c:44)
==9447==    by 0x10914E: str_new_from (str.c:56)
==9447==    by 0x10909F: split_impl (str.c:37)
==9447==    by 0x108E21: main (main.c:18)
==9447==
==9447== 26 (16 direct, 10 indirect) bytes in 2 blocks are definitely lost in loss record 4 of 4
==9447==    at 0x4C2CEDF: malloc (vg_replace_malloc.c:299)
==9447==    by 0x1090DA: str_new (str.c:44)
==9447==    by 0x10914E: str_new_from (str.c:56)
==9447==    by 0x109032: split_impl (str.c:30)
==9447==    by 0x108E21: main (main.c:18)

Donc en gros la mémoire perdue vient de set_impl (qui alloue priv->str) et de la ligne 44 de str_new (qui alloue priv).
Du coup ça correspond à l'idée que ton free dans list_del_with_free ne libère que str_t mais leak le priv et le priv->str.

Dernière modification par grim7reaper (Le 15/01/2018, à 13:39)

Hors ligne

#119 Le 15/01/2018, à 14:26

Dafyd

Re : /* Topic des codeurs [9] */

Salut Grim,

Effectivement, j'avais fini par trouver tout seul, mais j'ai oublié de poster ici, désolé !
Merci vraz.

Autre question, comme tu le vois je tente d'émuler de la POO en C (juste pour le défi, je n'ai aucune contrainte sur le langage ni sur le paradigme de programmation). Je suis assez content de mon mécanisme à base de D-pointer et de structure privée pour l'encapsulation. Cependant, actuellement je ne fait pas d'héritage, je fais de la composition. J'ai tenté de faire de l'héritage avec le transtypage de pointeurs sur les structures, mais cela oblige à reporter toutes les méthodes des structures parentes dans les structures filles, ce qui est assez verbeux, et sujet aux erreurs. Connaîtrais-tu une meilleure façon de faire ?

Merci vraz

Hors ligne

#120 Le 15/01/2018, à 20:44

Dafyd

Re : /* Topic des codeurs [9] */

Bonjour le meilleur truc que j'ai trouvé pour l'héritage c'est ça :

#include <stdlib.h>
#include <stdio.h>

typedef struct base_t* base_t;
typedef struct derivated_t* derivated_t;

struct base_t {
    int a;
};

struct derivated_t {
    base_t base;
};

int main(void) {
    derivated_t derivated = malloc(sizeof(*derivated));
    derivated->base = malloc(sizeof(*(derivated->base)));

    ((base_t)derivated)->a = 45;

    printf("%d\n", ((base_t)derivated)->a);

    return EXIT_SUCCESS;
}

Tu en penses quoi ?

Dernière modification par Dafyd (Le 15/01/2018, à 20:47)

Hors ligne

#121 Le 16/01/2018, à 11:37

grim7reaper

Re : /* Topic des codeurs [9] */

@Dafyd : ça me semble pas déconnant
J’utiliserais moins de pointeurs cela dit (moins de malloc), mais c’est ça l’idée.

typedef struct base {
    int a;
} Base;

typedef struct derivated {
    Base base;
} Derivated;

Jpense que c’est la manière «standard» de faire, et les compilateurs C++ doivent faire plus où moins un truc équivalent niveau object layout.
Là où ça se complique c’est quand tu veux faire de l’héritage multiple…

En fait dans l'idéal, tu voudras sûrement même un jeu de macro pour cacher la misère (comme GTK ou là où je bossait avant).
Tu peux peut-être encore améliorer le truc en utilisant les structures anonymes (mais ça risque de nécessiter des macros/de la génération de code).

Hors ligne

#122 Le 16/01/2018, à 11:57

Dafyd

Re : /* Topic des codeurs [9] */

D'accord merci. Pour l'héritage multiple, je suppose qu'il va falloir une macro pour caster la structure fille vers un offset dans elle même, du genre :

typedef struct parent1_t* parent1_t;
typedef struct parent2_t* parent2_t;
typedef struct fille_t* fille_t;

struct parent1_t {};
struct parent2_t {};

struct fille_t {
    parent1_t parent1;
    parent2_t parent2;
};

int main(void) {
    fille_t fille = malloc(sizeof(*fille));
    fille->parent2 = malloc(sizeof(*(fille->parent2)));

    parent2_t parent2 = (parent2_t)(fille + sizeof(parent_1));

    return 0;
}

Pas testé, mais ça devrait le faire non ?

Hors ligne

#123 Le 16/01/2018, à 13:26

grim7reaper

Re : /* Topic des codeurs [9] */

Ou tout simplement

parent2_t parent2 = fille->parent2;

Mais bon du coup t’as aucune abstraction en l'état (faut connaître le nom du champs pour caster), tu peux peut-être un récupérer un semblait avec des macro pour cacher les bidouilles internes.

Après tu peux t’amuser avec les fonctions virtuelles aussi, genre tu peux appeler les méthodes de parent1_t sur fille_t, sauf si fille_t la redéfinit dans ce cas faut appeler l’implementation de fille_t : en gros gérer une vtbl à la mains (ce que le compilo’ fait pour toi normalement).

Hors ligne

#124 Le 16/01/2018, à 16:00

Dafyd

Re : /* Topic des codeurs [9] */

Mhhh tout ça se transforme doucement en usine à gaz... Moi qui justement aime le C pour sa simplicité, à côté du C++, très complexe.
Je vais peut-être revenir à du C impératif, sans POO. Si je passe 90% du temps de codage à émuler une mécanique qui complexifie l'ensemble, je vois pas trop l'intérêt !

Hors ligne

#125 Le 16/01/2018, à 16:57

grim7reaper

Re : /* Topic des codeurs [9] */

Ha bah clairement, faire de la POO en C c’est sympa pour s’amuser/voir comment ça fonctionne sous le capot.
Mais à l’usage c’est lourd je trouve.
Si tu veux vraiment faire de l’OO utilise un langage qui fait ça nativement (genre C++), ça sera plus simple/puissant/moins buggé/plus lisible.

L’OO c’est pas la solution absolue, y’a des fois ça simplifie le code, des fois non. Mais si tu dois recoder tout le trucs à la main pour faire de l’OO, ouais clairement ça va pas te simplifier la tache tongue

Hors ligne