Tu sembles n'avoir pas tout à fait compris l'idée de ce qu'est une map...
Une map te permet d'associer une clé à une valeur (et de retrouver facilement la valeur qui correspond à une clé donnée). La clé correspond à ton "critère de sélection", alors que la valeur correspondra à ... l'élément que tu pourras utiliser dans ton code (si tu a trouvé l'élément correspondant à la clé que tu recherchais).
Pour ce qui est de la clé, on va essayer d'utiliser un type de donnée pour lequel le processus de comparaison (et surtout le processus qui permet de déterminer si la clé A est plus petite que la clé B (*) ) est le plus simple possible, car plus ce processus sera rapide, plus on pourra accéder rapidement à la donnée qui nous intéresse.
Ainsi, nous pourrions utiliser une chaine de caractères pour la clé. Dans le cas présent, cette chaine de caractères pourrait représenter le nom du callback que tu souhaite appeler.
Mais le processus de comparaison de deux chaines de caractères est particulièrement lent, car il s'agit de comparer chaque caractère d'une chaine au caractères qui se trouve à la même place dans l'autre chaine.
Si bien que l'idéal, c'est d'utiliser des valeurs numériques entières, car le processus de comparaison est très rapide : il suffit de comparer une valeur numérique à une autre
te permettra de sélectionner très rapidement n'importe quelle fonction qui s'attend à recevoir les paramètres du type indiqué qui se trouve dans la map.
La seule chose, c'est que travailler avec des valeurs comme 1,2,3,4 ou 5 dans le code, ce n'est pas particulièrement parlant (on parle de "valeur magiques" parce qu'on ne sait pas d'où elles sortent; peut être du chapeau d'un magicien ???) : l'utilisateur de ces valeurs doit en permanence se souvenir de quelle valeur correspond à quel callback, et, pour peut qu'il s'embrouille un peu les idées, il risque encore d'appeler un callback différent de celui qu'il croyait appeler
L'idée est donc d'associer des valeurs numérique à des identifiants qui permettront de savoir exactement de quel callback il s'agit. Cela tombe bien, car C++ a justement ce qu'il faut pour le faire dans sa manche. Ce sont les énumérations.
Ainsi, nous pouvons dresser une liste des callabacks qui s'attendent à recevoir des paramètres d'un certain type sous une forme proche de
(qui n'aura aucun cout à l'exécution : la transformation de callback_X vers la valeur numérique associée se faisant lors de la compilation) et nous pouvons donc modifier la map pour qu'elle prenne la forme de
Grace à cela, tu peux envisager d'invoquer n'importe quel callback sous une forme proche de
void call(Callbacks toCall /* ,paramètres à transmettre */ ){
auto it = lamap.find(toCall);
/* nous ne pourrons appeler le callback que
* s'il exsiste réellement
*/
if(it!= lamap.end()){
it->second(/*paramètres à transmettre */);
}
}
et le tour sera joué
(*) Il faut savoir que std::map n'utilise que la comparaison "plus petit que" pour fonctionner: si A n'est pas plus petit que B et que B n'est pas plus petit que A, c'est que A est équivalent à B (traduction: on considère que A et B sont égaux)
J'ai moi-même mis en place une ==>implémentation du système de signaux et de slots<== qui utilise le C++ moderne, qui ne nécessite aucune compilation (si ce n'est pour compiler les exemples et les tests unitaires) et qui tient sur 200 lignes de code dans un unique fichier d'en-tête.
Tu pourrais peut-être prendre exemple dessus (n'hésite pas à poser des questions en cas de besoin )
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Merci pour tes explications mais la clé ne pouvait être l'objet que je manipulais ou même un enum ( bizarre ton truc ).
Elle devait être nécessairement une clé créée lors de l'ajout d'un callback sur une fonction setter particulière de cet objet.
Et donc l'objet ne suffisait pas à repérer le setter en question.
J'ai donc décidé de wrapper mes fonctions afin d'en faire des objets représentables dans une map et d'utiliser l'une d'entre elles ( le setter ) comme clé.
1- Tes fonctions sont des comportements. Les objets qui appliquent ces comportements sont donc des "somethingERS" (dig -->diggER, sleep ->sleepER ... set -> settER, get -> gettER) :D. Mais ce n'est qu'un détail
2- Fais attention au fait que std::function<void (Truc)> représente un type qui n'a rien du tout à voir avec std::function<void (Machin)>... cela pourrait te jouer des tours
3- xxxManager ... Ouh que j'ai horreur de ce terme!!!
C'est un terme qui ne veut plus rien dire tellement il est vague vu qu'il signifie "celui qui gère".
Du coup, chaque fois que tu voudras ajouter une nouvelle fonctionnalité de gestion de tes machins, quand tu vas réfléchir à "où est ce que je pourrais bien ajouter cette fonctionnalité?" tu vas apporter une réponse du genre de "mais c'est de la gestion de mes machin... le manager est tout indiqué pour le faire".
A la longue, ton manager sera comme l'hydre de lerne : un monstre avec tellement de tête que tu ne saura plus par où l'attaquer, et que tu auras peur de modifier, de peur de "tout casser" (et qui, bien sur, ne respectera ni le SRP ni l'OCP !!!)
Utilises des termes plus précis, ce qui te permettra de distinguer les différentes responsabilités de gestions (qui, le plus souvent, interviendront à des endroits différents de ton code):
tu veux créer ton machin? crée une classe Creator ou Factory
tu veux maintenir une liste de tes machins? crée un classe Holder
tu veux faire appel à ton machin? crée une class Caller
tu veux tester (l'utilité en générale de) ton machin? crée une classe Tester
...
Tu t'éviteras bien des arrachages de cheveux
4- Je rejoint tout à fait markand: le transtypage (quel qu'il soit, et surtout dynamic_cast) ne devrait jamais être utilisé.
Entre autre, parce qu'il brise l'OCP
De toutes façons, j'ai envie de dire que, comme tu es dans une logique générique, faire appel à un transtypage qui ne peut avoir lieu que lors de l'exécution n'a que peu de sens
5- Je ne nie pas l'utilité des std::shared_ptr. Mais ils nécessitent de tels processus pour fonctionner que leur utilisation devrait se limiter exclusivement aux cas où l'on n'a vraiment pas d'autre choix...
6- As tu jeté un oeil à mon implémentation de signaux et de slot (dont tu trouvera le seul fichier d'en-tête dont tu as besoin =>ici<==) ?
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
En revanche, je ne vois pas ce que l'Alias apporte. La première fois que je l'ai vu c'était dans le code de koala01.
tiré de c++reference:
Type alias is a name that refers to a previously defined type (similar to typedef).
There is no difference between a type alias declaration and typedef declaration.
C'est donc selon selon les préférences. Et comme il m'arrivera peut-être à nouveau de programmer en C...
De plus, en répondant aussi à koala01:
koala01 a écrit:
4- Je rejoint tout à fait markand: le transtypage (quel qu'il soit, et surtout dynamic_cast) ne devrait jamais être utilisé.
Entre autre, parce qu'il brise l'OCP
De toutes façons, j'ai envie de dire que, comme tu es dans une logique générique, faire appel à un transtypage qui ne peut avoir lieu que lors de l'exécution n'a que peu de sens
j'utilisais justement le dynamic_cast en me référant à ceci:
"When the compiler sees the definition of a template, it does not generate code. It generates code only when we instantiate a specific instance of the template."
Seulement à l'exécution mon template sera reconnu en effet. J'assume mon erreur.
Je suis au courant, au sujet des templates, qu'un passage de types différents implique des templates différents.
Je suis tombé sur cet article qui m'a l'air intéressant et plus généraliste...
J'étudie ton code Signal.hpp. J'ai du compiler TOUTES les bibliothèques nécessaires ( cmake, flex, doxygen ) car celles de debian sont obsolètes ( je suis sous stretch ), alors que ce n'était pas nécessaire...
Je le trouve complet par rapport à ce que je voulais faire. Voilà pourquoi je retardais ma lecture.
Je m'achète ton bouquin en espérant gagner en efficacité. Au fait, quel est le meilleur livre pour la librairie standard d'après toi? Ou bien me conseilles tu de me renseigner directement sur cppreference?
Je me suis procuré le bouquin de Koala01 alias "coder efficacement - bonnes pratiques et erreurs à éviter"
ainsi que le bouquin de Scott Meyers, j'ai nommé."Effective Modern C++".
ET ÇA VA DEVENIR MA BIBLE.
Avec sympathie
fonction find de std::map sur std::function
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
git is great because Linus did it, mercurial is better because he didn't.