Pour lui donner un maximum de chances, je crois qu'il faut prévoir les deux...
- ca doit être une librairie pour que des personnes utilisant un autre framework puissent malgré tout l'utiliser (ou s'interfacer avec),
- ca doit aussi être une librairie parce qu'il y a un paquet de programmeurs qui ne veulent pas entendre parler de framework
- mais ca doit également "pouvoir être" un framework pour simplifier la vie du developpeur de base qui a juste besoin d'une application avec IHM de base, et qui ne sait pas par quel bout prendre le problème : pas mal de libs graphiques sont des frameworks parce que, honnêtement (au moins sous windows, le reste je ne connais pas), les fonctions de bases de l'application graphique, c'est pas marrant à faire...
- et ca doit aussi être un framework si on veut le coupler avec du RAD, enfin je crois... une fois de plus, si on arrive à bâtir une librairie d'interface, en C++ moderne, qui exploite la puissance du langage, mais accessible au developpeur de base, on a un sacré truc!
Francois
On peut toujours s'occuper d'un framework une fois seulement la première version de la bibliothèque faite.
Mais a mon avis, discuter framework aujourd'hui est un peu vain.
De loin, on peut faire une bibliothèque suffisamment abstraite pour gérer toutes les variantes d'interfaces homme-machine (hors connectiques neurales qui viendront quand on aura des cerveaux boostés...quoi que?) avec d'un coté de quoi traiter des entrées, passer au milieu dans l'abstraction du code et puis sortir quelque chose sur les périfériques de sortie (ou pas d'ailleurs).
Ce qui me fait penser... quelqu'un a suggéré de plutot faire un générateur de code il me semble (j'ai pas tout relu et j'ai pas tout capté a toutes les discussions)... quelles sont les différences avec du code "généré" via templates / dsl? (a part le fait d'avoir le code généré dans un fichier à part)
Heu sinon je suis un peu en vrac mais je me disais que la bibliothèque pourrait simplement permettre de "générer" (a voir ce que ça veut dire) les interfaces et briques des deux autres parties ( entrées, sorties) , au lieu d'avoir des choses rééllement séparées.
Au passage, je ne vois pas le souci avec la 3D : au final, même avec un affichage 3D, ce n'est que la 2D représentée par la surface d'un écran qui compte comme "interface". Au delà même de ça, on peut oublier notion de surface du coté entrée+abstration parcequ'elle ne compte que pour le rendu/sortie.
A mon avis on gagnerai a s'interesser à la théorie "la plus abstraite" concernant les interfaces (peut être pas que homme-machine du coup?). Ca aiderai a fixer les limites de ce qu'il serait interessant de faire ici.
Bon je fatigue...
A vrai dire, je serais presque de cet avis aussi...
Nous pourrions parfaitement commencer, dans un premier temps, par implémenter:
en faisant en sorte d'inciter l'utilisateur à créer son interface en "beau C++" sous une forme proche de
- les "widgets" (à défaut de meilleur nom) de base (fenetre, menu, éléments de menus, boutons en tous genres, liste déroulantes, ...)
- le système de signaux
- Le systèmes d'entrées "de base" (j'ai dit clavier /souris )
- le système de rendu "simple"
Où MonMachin et MonTruc (selon exemple) pourraient parfaitement mettre en oeuvre des politiques de forme (triangle, rectangle, disque, polygone, étoie à cinq branches ou que sais-je), de "label" (le texte qui représente la donnée: modifiable ou non), de sérialisation ( (im)possibilité éventuelle d'envoyer la donnée dans un flux), j'en passe et de meilleures.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 class MaFenetre : public MainWindow { private: /* ou toutes ces classes seraient définies par ailleurs * par l'utilisateur ;) */ MonMenu menu; MonTruc truc; MonMachin machin; };
A partir de là, nous commencerions déjà à parler de manière un peu moins abstraite, et nous pourrions donc commencer à voir comment articuler le reste autour de tout cela
Justement, je veux éviter d'avoir à lancer un "truc en plus" afin de pouvoir créer l'IHM... Comme je l'ai expliquer, la création d'une application disposant d'une IHM ne doit absolument pas demander au développeur de changer ses habitudes de compilationCe qui me fait penser... quelqu'un a suggéré de plutot faire un générateur de code il me semble (j'ai pas tout relu et j'ai pas tout capté a toutes les discussions)... quelles sont les différences avec du code "généré" via templates / dsl? (a part le fait d'avoir le code généré dans un fichier à part)
Maintenant, comprenons nous aussi... je n'ai rien contre l'idée qu'une partie RAD ne nous crées automatiquement des fichier d'en-tête et d'implémentation, ou qu'ils y ajoutent automatiquement des déclarations / définitions de fonctions... C'est le but de ce genre d'outils
Mais, et je tiens à cet aspect, il y a une différence entre le fait de permettre à une "application tierce" de rajouter/ retirer des informations dans un fichier (d'autant plus que, dans mon esprit, le "dessin" de l'interface et l'aperçu du code sources sont deux parties intimement liées dans un RAD ) et le fait de se reposer sur une application tierce pour générer, sur base de l'existant, des fichiers qui seront invalidés lors de la compilation suivante
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Ok, je commence a saisir la problématique du widget.
En effet, je pense pas qu'il y est moyen de totalement passer outre ces dit widget. Seulement, on l'éclate dans la pratique notre widget... Pourquoi ne pas bannir LES widgetS concret, au profit du Widget !
Comprenez ici Widget comme étant une classe à utiliser, probablement former d'une composition NON PAS d'autre widget, mais de classe zone/data/view/event.
Ainsi, je pensais plus a une truc du genre :
C'est sommaire, mais ça coupe le prémaché-pas-maniable-du-tout.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 int main() { //... Widget MainWindow(aZone,aData,aView,aEvent); //... }
Après, composite ou pas, c'est une autre question. A priori, je suis ni pour ni contre.
The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
--Wilhelm Stekel
Klaim a écrit :
"
quelles sont les différences avec du code "généré" via templates / dsl?
"
Avec un generateur de code, pour l'utilisateur:
1/la syntaxe d'écriture est + claire qu'avec des template /dsl (puisque elle est choisi selon le projet).
2/ la syntaxe de relecture (ex: lorsqu'on debug) est + claire qu'avec des template /dsl : au lieu de se balader dans des template sendMessage<U,C>(), on a le vrai nom de la classe sendMessage_EntryGame_To_RenderEngin(), qui est un truc parlant. Perso, je genere aussi des commentaires (format doxygen, mais aussi au milieu des fonctions).
4/ il est beaucoup plus simple de gérer des compilateurs recalcitrants.
5/ temps de compilation plus courts qu'avec des templates, mais + longs qu'avec des macro.
J'utilise boost::spirit pour faire mes parsers, et je design un langage specifique selon le projet. En general, le code genere contient juste un peu de template, rarement des macros (ex: celles de boost), et est très simple à relire.
Pour info, j'ai lu sur un forum que Joel Falcou (que je vois souvent participer à la developer mailing list de boost) generait ses template de function programming en les ecrivant en OCaml. Bref, il a choisi un outil adapté.
@koala01
je ne vois pas le rapport entre ton bout de code et de la programmation generique .
Très sincèrement, je pense qu'il serait + enrichissant de se dire : A quoi ressemble un système idéal/parfait?
Pour moi le système parfait (sans generateur de code), fait la chose suivante:
J'ai des classes d'une de mes applis qui existent deja (SceneGraph, Bones, Texture, Compta_Salarie) et je souhaite les manipuler par l'intermediaire d'une GUI.
1/ je declare ces classes a mon systeme, et les fonctions concernees + attributs membres.
IHMregister(SceneGraph,(
SceneGraph::rootNode,
SceneGraph::NbElement,
SceneGraph::destroyNode)
);
IHMregister(Texture, (Texture::buffer))
IHMregister(Compta_Salarie, plein de champs a la con );
2/ je lui dit quelle est la classe d'origine.
Gui lGui = CreateIHMFor(SceneGraph);
3/ par defaut la methode cela affiche un ignoble 'propertygrid'.
lGui.editMode()
dans ce mode, je peux faire clic droit sur mes types/objets, et modifier le type d'affichage proposes par le moteur de rendu (onglet, popup, liste de selection),
les parametres visuels (fonts, colors, border...),...
Le tout est sauvegardé dans lGui. Que je peux ensuite enregistrer dans un xml.
lGui.dumpTo("monxml.xml");
Plus tard, je peux lancer une appli avec :
Gui lGui = CreateIHMFor(SceneGraph, "monxml.xml");
désolé faut que je retourne bosser la suite plus tard .!!
C'est presque imparable, comme logique... et ca nous mène... au superWidget dont on voulait se défaire.
Depuis le début, un truc me travaille. Toute la discussion sur les widgets, et le fait qu'on y revient sans cesse, montre qu'on a bien un concept central, qui normalement devrait s'incarner en une classe de base....
Oui mais, en C++, une classe de base, ca ossifie drolement le système. Alors on voudrait en faire une, sans trop la faire... Et on va utiliser des astuces du langage (dit C++ moderne) qui permettent de s'en abstraire (de manière un rien sale et gratuitement abstraite, mais on n'a rien sans rien)
Quelque part, j'ai l'impression qu'on met en évidence les limites du langage, et de sa façon d'implémenter les "concepts" du monde réel par des classes.
Ce qui amène deux questions :
1- si on a bien un "concept" central, le widget, qu'on ne représente pas par une classe, on fait comment en C++ moderne?
2- et si on conclut que ca va être moche, faut il passer à un autre langage (c'est comme ca que je lis les remarques sur la génération de code...)
Francois
Eh bien on utilise les concept du C++0x. Zut, ils ont été retirés …
Mais on peut faire comme si ils étaient là. Imaginons en effet que la fonction qui fasse le layout des widgets enfants d'un widget soit une fonction template. Elle sera instanciée avec nos objets, qui devront avoir les fonctions membres width(), height(), x() et y() définies (j'invente là), et c'est tout. La classe mère widget sert en fait sous cet angle à définir ces fonctions membres de base de manière concrète afin de pouvoir compiler la fonction de layout de manière concrète sur des classes de widgets qui n'existent pas (pas encore).
Dans le cas d'une fonction de layout templatée, la compilation n'aura lieu qu'avec nos widgets personnels, qui devront implémenter les fonctions membres nécessaires, mais qui ne devront pas forcément hériter d'une classe de base et surcharger ces fonctions.
Didier
Je crois que l'on ne pourra pas se défaire du widget en tant que concept, mais on peut s'en défaire en tant que classe de base non template...
Faut-il rappeler que, si MaClass est une classe template, que Truc dérive de MaClass<T> et que Machin dérive MaClass<U>, Truc et Machin font partie de hiérarchies d'héritage distinctes
Tu obtiens deux classes présentant, effectivement, une interface similaire (héritée de MaClass et spécialisée pour T dans Truc et pour U dans Machin), mais tu ne pourra jamais faire cohabiter Truc et Machin dans une collection commune.
Si (et cela répondra quelque part à la question de ElPedro ) nous en arrivons au final à une classe de base proche de
et que tous les éléments visuels héritent de cette classe sous différentes spécialisation (CRTP insinde ), cela ne me pose strictement aucun problème de conscience, pour autant que l'on évite, une fois la spécialisation donnée, de repartir sur de véritable pyramides de Chéops en terme d'héritage
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 template <class ShapePolicy, // rectangle, triangle, polygone... ou étoile à cinq branches class DrawPolicy, // tout ce qui a trait au tracé et au redimentionnement class PointingPolicy, // tout ce qui gère le système de pointage class PersistancePolicy, // création et lecture de/dans un fichier class ChildrenPolicy, // ca peut avoir des enfants d'un type donné... ou non... class JenOublieSurementPolicy //tout le reste, surement mieux séparé :D > class widget { /* son contenu */ };
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
A priori, je ne pense pas que cela sait très user-friendly si en tant qu'utilisateur je dois donner 6 noms de classes comme paramètres au template dont j'hérite pour définir ma classe de widget.
Mais d'un autre côté, c'est vrai que cela a l'air assez efficace si c'est tout ce que j'ai à écrire. Ça risque juste de devenir un poil pénible à écrire pour tous mes widgets, et donc il faudrait en mettre certains (des paramètres de template) par défaut. mais dans quel ordre ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part class MonWidgetAMoi : public widget<Rectangle, Resizeable, NoKeyboard, NoPersist, ChildStackHoriz>
Je réfléchis tout haut : et pourquoi pas ça*:
À nouveau, dans le cas où les fonctions et classes de la librairie sont pour la plupart définies sous forme de template ces fonctions peuvent travailler avec des classes, pourvu que ces dernières définissent les membres qui vont bien. L'utilisateur n'écrit que du code plus simple, sans <>.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 class MonWidget { public: Rectangle shapePolicy; Resizeable resizePolicy; LayoutStackVert layoutBase; };
Didier
Pas dans mon sens : il s'agit bien de voir la classe widget comme une classe simple, pas la base d'un héritage.
En fait, je trouve révoltant même l'idée de proposé "le" slider/button/etc... Juste qu'il semble difficile de se défaire de la notion de widget. Et les héritages trop complexe me donne des boutons >< !
La version template me semble encore plus prenante : widget devient un concept abstrait rien d'autre, né de la réunion de plusieurs comportement défini.
A ta première question, je dirai donc qu'il y a vice de forme : pourquoi vouloir bâtir un héritage sur ce qui semble être une simple composition. La seul vrai utilité de la classe widget au final, c'est donc dans se cas d'assurer des pré-requis entre différente association. Rien d'autre.
Suite à ça, un "état" de l'application peut être : une liste de fonction de dessin + une liste associative de zone/event.
The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
--Wilhelm Stekel
Le fait est qu'à partir d'une telle classe, il est tout à fait envisageable que la bibliothèque en propose une autre sous la forme de
qui, elle, sera parfaitement user-friendly
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 template <class Shape> class ResizableButton { public: /* les comportements qui vont bien * et qui font appels aux comportements de data_ */ private: typename widget<Shape, resizable, keybord_and_mouse, nopersist, nochild> data_; }
C'est toute la beauté de la programmation générique, si elle est bien mise en oeuvreMais d'un autre côté, c'est vrai que cela a l'air assez efficace si c'est tout ce que j'ai à écrire.Je crois que je viens de répondre à cette question, dans une certaine mesureÇa risque juste de devenir un poil pénible à écrire pour tous mes widgets, et donc il faudrait en mettre certains (des paramètres de template) par défaut. mais dans quel ordre ?
Par la suite, libre à toi, si tu le souhaites, d'utiliser la version template<Rectangle> ou d'hériter de cette classe si tu souhaite l'améliorer
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
C'est justement ce qui me dérange. Dans ce que tu proposes, la programmation générique est utilisée pour faire principalement des héritages (encore des héritages, toujours des héritages). Elle ne sert pas qu'à ça, bien au contraire. AMHA, le principe des conteneurs, des itérateurs et des algorithmes dans la librairie standard est simplement fabuleux. Et il n'y a aucune notion d'héritage dedans ! Rien que des combinaisons de fonctions génériques qui utilisent des classes qui doivent fournir certaines fonctions bien précises.
Ainsi, j'ose imaginer une librairie dans laquelle seraient disponibles des algorithmes de layout. Ces algorithmes (des fonctions génériques) utiliseraient des shapes que chaque widget (défini par l'utilisateur ou widget fourni de base) définirait comme membre et fournirait au travers d'une fonction shape(), par exemple. D'autres fonctions génériques pourraient aussi utiliser cette shape pour effectuer le tracé du widget.
De manière orthogonale aux shapes, chaque widget peut offrir une facette d'input par exemple. Charge à des fonctions génériques gérant les input de rediriger le clavier et la souris vers les input des widgets qui l'ont demandé.
Didier
Il est vrai que j'ai présenté les choses sous un angle relativement restreint, et qu'effectivement, il y a d'autres possibilités encore bien plus intéressantes que celles que j'ai proposées.
C'est, simplement, parce que je n'ai pas encore réellement réfléchi à la problématique, et que je présente au final des solutions "naïves".
Mais, il est tout à fait vrai qu'il y a quantité d'autres possibilités à envisager avec les template
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.
Une impression:
A force de découper, es ce que cela ne va pas tendre vers une multitude de mini SuperObject(toutes les policy) et devenir le bordel?
Car il risque d'y avoir certaine policy incompatible avec d'autre policy?
En créant une politique par aspect orthogonal, tu auras, effectivement, un nombre sans doute important de traits et de politiques et, très certainement, un nombre (très) important de combinaisons possibles, mais ca apportera une souplesse incroyable au niveau de ce qu'il devient possible de créer, sans forcément mettre le bordel dans le tout
Le risque est là, il ne sert à rien de se le cacher... à nous de faire en sorte de le limiter au maximumCar il risque d'y avoir certaine policy incompatible avec d'autre policy?
Si on sépare correctement les différentes politiques (comprend: si on veille à ce qu'une politique donnée n'ait effectivement qu'une responsabilité clairement définie), le risque d'incompatibilité devient faible...
Il restera toujours le risque de voir quelqu'un définir un objet aux comportements "pas vraiment cohérent" de prime abord, mais, s'il en a réellement besoin, il ne nous appartient pas forcément (en tant que développeur de la bibliothèque) d'en juger
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
J'ai regardé rapidos les sources de Adobe ASL, essentiellement la partie GUI.
Les widgets sont indépendants, ils ne reposent pas sur une base class commune, même s'ils exposent la même interface (display(), measure(), enable()...). Il y a une implémentation complète de chaque widget pour chaque plateforme (win32 et Carbon (mac)). Chaque widget créé enregistre sa propre "window proc" (subclassing) et les events jugés intéressants pour un widget donné sont "hard codés" au sein du widget. Je me trompe peut être, mais j'ai comme l'impression qu'il n'est pas prévu de customiser le comportement des widgets par rapport aux events de l'OS d'un point de vue utilisateur.
Adobe fournit son propre type à la Boost.Any (any_regular). Par exemple le widget "tab_group" embarque un vector de any_regular's. L'aspect "composite" passe donc, non pas par une base classe commune, mais un "Boost.Any".
Je comprends bien, mais je suis un peu sceptique... N'est on pas en train de remplacer une hierarchie de classes à, disons 3 ou 4 niveaux (si on veut être raisonnable), mais assez simple, par une non hierarchie dans laquelle on a une demi douzaine (voire davantage) de policies...
Et si d'aventure on se rend compte que tous les widgets partagent un certain nombre de policies, a-t-on réellement changé le principe, ou juste remplacé une syntaxe imparfaite par une autre?
On pourrait bien se retrouver avec une "pseudo hiérarchie", templatisée, avec des widget qui contiennent un certain nb de policies de base (la "novclasse" de base), puis en dessous d'autres qui en ont quelques unes en plus, et ainsi de suite...
J'ai l'impression que quelque chose cloche dans cette description, mais je n'arrive pas à dire quoi exactement...
Et en essayant de me mettre dans la peau d'un utilisateur (forcément un peu rompu aux frameworks à base d'héritage), je me demande si ce type d'approche ne risque pas d'être déroutante, voire lourde à mettre en oeuvre.
Ne paye-t-on pas un peu cher la souplesse qu'on gagne avec ce modèles "ultra paramétré"?
Francois
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager