Bonjour,
si un attribut doit être constant, mais qu'il doit être initialisé dans le corps du constructeur, comment fait-on ? const_cast ?
Qu'est-ce qui est recommandé ? J'avoue avoir du mal à placer un const_cast vu la réputation qu'il a.
Bonjour,
si un attribut doit être constant, mais qu'il doit être initialisé dans le corps du constructeur, comment fait-on ? const_cast ?
Qu'est-ce qui est recommandé ? J'avoue avoir du mal à placer un const_cast vu la réputation qu'il a.
pourquoi doit-il etre initialise dans le corps du constructeur?
dans tous les cas ou ca semblait impossile, j'ai reussi a l'initialiser quand meme en faisant une fonction libre. Peux tu montrer ton exemple?
il doit être déterminé dans le constructeur parce sa valeur est déterminée par un algorithme (avec une connexion réseau) qui manipule l'objet lui-même. Une fonction libre ne fonctionnerait je pense pas puisque déterminer cette valeur nécessite d'utiliser l'objet lui-même... or pour pouvoir l'utiliser il faut qu'il soit initialisé... non ?
As-t-on le droit de modifier un objet en cours d'initialisation (entendez par là que la liste d'initialisation n'est qu'à moitié exécutée) ?
L'exemple épuré :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 class Network { public: Network(sf::IpAddress join); Network(const Network&) = delete; virtual ~Network(); private: const ServerId m_id = 1; std::map<ServerId, std::unique_ptr<sf::TcpSocket>> m_otherServers; }; Network::Network(sf::IpAddress join) { //l'algorithme doit se connecter et donc modifier m_otherServers //afin de récupérer la valeur attribuée par le serveur distant à m_id }
dans ce cas, je creerais un objet intermediaire que je copierais dans un champ const
//edit
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 class IntermediateNetwork { friend class Network; public: IntermediateNetwork(sf::IpAddress join); IntermediateNetwork(const Network&) = delete; virtual ~IntermediateNetwork(); private: ServerId m_id = 1; std::map<ServerId, std::unique_ptr<sf::TcpSocket>> m_otherServers; }; IntermediateNetwork::IntermediateNetwork(sf::IpAddress join) { // ton code } class Network { public: Network(sf::IpAddress join); Network(const Network&) = delete; virtual ~Network(); private: const IntermediateNetwork m_implementation; }; Network(sf::IpAddress join) : m_implementation(join) { // maintenant le IntermediateNetwork est const // donc le m_serverId aussi }
on peut modifier un objet en coursde construction a condition qu'il ait deja ete construit, l'exemple marcherait comme ca:
je ne suis pas sur d'avoir compris exactement quel champ est const et doit etre modifie mais j'espere que ce que j'ai dit ci dessus est assez clair.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 class Network { public: Network(sf::IpAddress join); Network(const Network&) = delete; virtual ~Network(); private: // ATTENTION, les initialisations sont faites dans l'ordre // de leurs declarations dans la classe, donc mettre le // champ modifiable en premier std::map<ServerId, std::unique_ptr<sf::TcpSocket>> m_otherServers; const ServerId m_id = 1; static ServerId doConnectToIP(std::map& serverList, sf::IpAddress join); }; Network::Network(sf::IpAddress join) : m_otherServers() , m_id(doConnectToIP(m_otherServers, join) { }
Dernière modification par screetch ; 21/10/2012 à 23h26.
Euh... le premier exemple ne résout pas le problème puisque tu essaies toujours de modifier un const dans le constructeur de Intermediate
Par contre ton 2e exemple me semble plus pertinent...
ah pardon, je voulais retirer le const de IntermediateNetwork.
Ensuite tu constifies IntermediateNetwork ce qui rend par ricochet tous les champs constants.
Edit numero 2![]()
le problème étant que j'ai besoin de m_otherServers en non-const par la suite...
edit: tout ça pour un misérable const intpeut-être que la meilleure solution serait de virer le const tout simplement ?
(ça résout ce problème particulier mais pas le cas général)
edit2: j'aime bien le verbe constifier, ça fait un peu pétrifier ou constiper...![]()
Pourquoi ne pas pousser plus loin la proposition de screetch ?
Tu crées une première classe qui s'initialise, qui serait identique à ce que tu as aujourd'hui mais avec aucun attributs const.
Et ta vraie class comme tu le souhaites avec les constances de ton choix pour chaque attribut. Leur initialisation se faisant par la liste d'initialisation, donc plus de problème![]()
Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
Un peu de programmation réseau ?
Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.
Salut,
A vrai dire, je me demande pourquoi tu voudrais rendre m_Id "uniquement" constant...
Ce faisant, c'est comme si tu disait:
Mais...Je vais avoir une foule d'instances de Network et toutes ces instances vont avoir la même id
Soit c'est vrai, et dans ce cas, ton id a de grandes chances de pouvoir être static, constant et, pourquoi pas, publique (dés le moment où il est ... constant et statique), car m_id est simplement un invariant dont tu peux souhaiter disposer à "n'importe quel moment" depuis "n'importe où", indépendant de toute instance de ta classe.
Soit, tu peux, effectivement, avoir plusieurs réseaux ayant chacun un id différente, et, dans ce cas, l'id du réseau sera déterminée "par ailleurs"...
Dans ce cas, ton id n'a absolument pas à être constante, il faut juste veiller à ce que les fonctions qui utiliseront id soient constantes.
De toutes façons, une fois que m_otherServers aura été remplie, tu ne devras plus modifier ton instance de Network, sauf cas exceptionnel où tu voudrais ajouter / supprimer un autre serveur, voir mettre la liste des serveurs à jour !
En déclarant toutes les fonctions qui n'ont rien à voir avec la mise à jour de la liste de serveurs constantes, tu auras la certitude qu'elles ne pourront modifier ni m_id ni m_otherServers.
Au final, au pire, tu auras quelque chose comme
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 class Network { public: Network(sf::IpAddress join); /* les seules fonctions non constantes à avoir */ void addServerToList(serverId ); void removeServerFromList(serverId); void updateServerList(); /* et toutes les fonctions qui n'ont rien à voir avec la mise à * jour qui sont constantes */ void foo1() const; void foo2(/* ... */ ) const; /* ... */ void barN() const; /* mais SURTOUT PAS LA FONCTION void setId(ServerId newid) { m_id =newid; } */ private: ServerId m_id = 1; std::map<ServerId, std::unique_ptr<sf::TcpSocket>> m_otherServers; };
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
l'utilisation de m_id est tout à fait localisée à cette classe.
L'idée c'est de rejoindre un réseau, celui-ci nous attribue un ID qui sera fixe jusqu'à la fin du programme/crash du serveur. Network est la classe qui gère les échanges réseau, elle-seule a besoin de cet ID pour le fixer en en-tête de tous les messages et le comparer avec les entêtes des messages reçus afin de ne pas faire transiter des messages déjà traités. De plus l'ajout/suppression d'un autre serveur peut surgir n'importe quand. La suppression particulièrement parce que toute fonction rencontrant une erreur sur une communication détruira la connexion avec le serveur correspondant, le considérant comme mort.
P.S: ton code contient toujours un const sur le m_id![]()
Je dirais que c'est une raison de plus !
Tu crées une fonction "connectNetwork" (par exemple), non constante et privée, qui se chargera de se connecter, d'obtenir l'id de ton instance, et de remplir ta liste de serveurs, qui n'est appelée, dans un premier temps que dans les consructeurs, puis tu n'y touche plus!
Note que, à bien y réfléchir...
On peut se poser la question de savoir "que va-t-il se passer si le serveur crashe" au niveau de ton application.
Si tu dois faire le tour de tous les pc sur lesquels ton application tourne pour la relancer suite à un crash serveur, ca va pas forcément être folichon
On pourrait donc parfaitement envisager d'apporter une certaine "tolérance" face au crash serveur en faisant en sorte que, si l'application se rend compte que le serveur ne répond plus (quelle qu'en soit la raison), elle tente de se reconnecter à intervalle régulier.
Mais, si c'est le cas, aussi bien l'id que la liste des autres serveurs risquent d'être différents après la reconnection, et il faudra donc mettre tout cela à jour.
Il serait peut etre donc aussi intéressant de prévoir une fonction publique, non constante, "reconnect" qui vide la liste des serveurs et qui invoque par la suite la fonction connectToServeur
Je réfléchis peut etre trop loin, mais c'est sans doute une réflexion que tu finiras sans doute par avoir tot ou tard
Juste une erreur de copier / collerP.S: ton code contient toujours un const sur le m_id![]()
... Elle est corrigée
(et le code a d'ailleurs été un peu modifié
)
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
La tentative de reconnexion régulière serait une grosse faille de sécurité. Ça serait la même chose que de laisser un listener ouvert en permanence pour accéder au réseau.
On ne fait une connexion que sur ordre express de quelqu'un d'humain, ou d'un script externe ayant accès à l'entrée standard du programme.
De plus à la connexion il n'y a pas qu'une simple connexion, il y a (en gros) : connexion à un serveur qui va rediriger cette connexion après vérification des mots de passe etc. vers plusieurs serveurs auquel le nouveau serveur devra se connecter, puis équilibrage de la charge de travail entre les différents services.
Si un serveur est déconnecté d'un serveur mais pas d'un autre, alors il demande simplement un rééquilibrage des connexions (pas nécessairement avec le serveur précédent).
Je me suis peut etre mal exprimé, mais l'idée est de savoir ce qui se passe si le serveur crashe...
Tu seras sans doute d'accord avec moi que faire planter purement et simplement l'application n'est sans doute pas une solution acceptable !
Tout comme tu seras sans doute d'accord avec moi pour dire que, même si tu tentes de te connecter sur un autre serveur, la logique apportée par la fonction reconnect est, hormis le choix du serveur, sensiblement identique à ce que j'ai décrit
Tu as raison de pinailler sur la sécurité, mais, même si c'est sur une intervention de l'utilisateur que la reconnexion s'effectue, il est sans doute bon de prévoir le moyen de la provoquer (ou de provoquer le rééquilibrage des connexions)![]()
[EDIT] et que cette reconnexion / ce rééquilibrage peut parfaitement provoquer un changement de réseau et donc d'id![]()
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
Ce qu'il faut, surtout, comprendre, c'est que, dans l'absolut, ce n'est pas le membre m_id qui doit être constant, mais l'utilisation globale (hors cas particuliers) de ton instance qui doit travailler "comme si l'instance était constante" et que la grosse majorité des fonctions peut donc être déclarée comme constante, car s'engageant à ne pas modifier l'état de l'instance au départ de laquelle elles (les fonctions membres) sont appelées.
A coté de cette majorités de fonction déclarées constantes, on peut entrevoir "quelques" fonctions qui justifient de modifier l'état de l'instance de Network au départ de laquelle elles sont appelées.
ces quelques fonctions seront celles de (re) connexion et de mise à jour de la liste des serveurs.
L'avantage de déclarer les fonctions "qui n'ont aucune raison de modifier l'état de l'instance au départ de laquelle elle sont appelées" comme étant constante, c'est que si tu viens, dans l'une de ces fonctions, à invoquer (par mégarde, sans doute) une fonction non constante (et qui risque donc de modifier l'état de l'objet courent), le compilateur t'insultera haut et fort![]()
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
Petite parenthèse sur un aspect important que je n'ai pas vu abordé -- j'ai lu en diagonale.
-> Les tests.
Si tu veux pouvoir tester facilement ta classe, fait de l'id un truc donné à la construction et non un truc calculé à la construction en allant taper dans une ressource globale et quasi-externe.
Ainsi tu pourras mettre tous les const que tu veux -- à bon ou mauvais escient --, mais surtout tu seras en mesaure d'écrire des test-unitaires et autres tests fonctionnels/d'intégration de plus haut niveau.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
+1...
En fait, je venais de penser à une question qui n'a pas encore été abordée... : Quelle est la responsabilité réelle de ta classe Network
J'aurais tendance, à voir ses membres privés, à dire que sa responsabilité est de maintenir une sorte de "mapping" du réseau.
Si je ne me trompe pas, alors, la responsabilité de la gestion de la connexion n'échoit, en vertu du principe de la responsabilité unique, pas à ta classe Network, mais bien à "quelque chose d'externe", à savoir sans doute une classe "Connexion" (ou similaire).
Et la responsabilité de cette classe n'est d'ailleurs que de "maintenir la connexion active" (ou de la réactiver en cas de besoin, selon le genre de projet auquel tu as affaire).
Dans ce cas, c'est, toujours en vertu du principe de la responsabilité unique, sans doute une troisième classe ( "RequestSender" ou un de ses dérivé si c'est une classe abstraite) qui utilisera la connexion pour envoyer la requete permettant d'obtenir l'id du réseau / la liste des serveurs opérationnels.
Et c'est cette classe là (ou les classes qui en dérivent) qui devrait être utilisée par ta classe Network aussi bien pour déterminer la valeur de m_id que le contenu de m_otherServers.
A ce moment là, rien ne t'empêcherait, a priori, de déclarer m_id comme étant constant, car tu peux utiliser son (pseudo) constructeur en appelant la classe (dérivée de) RequestSender pour obtenir l'id du réseau.
Ton constructeur pourrait donc ressembler à quelque chose comme
Après, il reste le problème de "quoi faire en cas de déconnexion"...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class Network { public: /* on considère que IdRequestSender dérive de RequestSender * et que ces classes sont en mesure d'obtenir ou de de mettre une * connexion en place en n'ayant que l'ip à contacter, mais ca, c'est * un autre problème ;) */ Network(sf::IpAddress join): m_id(IdRequestSender(join).send().toint()) { } private: const ServerId m_id = 1; std::map<ServerId, std::unique_ptr<sf::TcpSocket>> m_otherServers; };
J'ai plaidé pour le fait qu'il était sans doute préférable de tenter une reconnexion, quitte à ce que ce ne soit que sur intervention de l'utilisateur ou à ce que ce ne soit en fait qu'un "rééquilibrage" de la connexion.
Mais le problème est de savoir si l'on va réellement garder la même valeur pour m_id ou s'il est acceptable de détruire purement et simplement l'instance de Network pour en recréer une avec une nouvelle valeur de m_id si celle-ci vient à changer.
Je ne connais simplement pas la réponse à cette question car je n'ai pas une connaissance suffisante de ton projet pour y répondre, mais je te conseille vraiment de réfléchir à ce qu'il est acceptable de faire si la valeur de m_id vient à changer
S'il est acceptable de détruire ton instance de Network et d'en recréer une, pas de problème, laisses donc m_id constant, mais, si tu viens à estimer que c'est inacceptable, tu n'auras pas le choix: il faudra forcément le rendre non constant et te reposer sur la constance des fonctions membres de ta classe Network pour s'assurer qu'il ne sera pas modifié inopinément![]()
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
Merci pour vos reponses et désole de l'absence des miennes car je suis privé de connexion internet pour une raison indéterminée. (suis sur mon mobile)
Tout d'abord il ne doit y avoir qu'un seul réseau. Un serveur est soit sur le reseau, soit éteint.
Il s'agit d'un réseau mesh dont tous les serveurs disposent du même code source.
Pour ce qui est de la connexion perdue, elle le reste. Si un serveur vient à se trouver en dessous du seuil minimum de connexion, il demandera un rééquilibrage des connexions.
Mais en aucun cas un serveur ne peut changer d'id (c'est comme votre n¤ de sécu).
De plus un serveur ne se connecte qu'une seule fois.
Mais en effet c'est peut-être à la classe ConnectionService (elle existe) de s'en occuper... Le problème c'est que cette classe est un Service, qui a donc besoin d'une reference sur Network pour s'initialiser... Bref on tourne en rond![]()
Il est à noter que je n'ai présenté plus haut qu'une (petite) partie de la classe Network qui a en charge de recevoir les messages et de les envoyer au service concerné ainsi que de faire circuler les messages entre les serveurs, le tout en multithreadé.
Hé bien, peut etre que le problème vient justement de ta classe ConnectionService...
Poses-toi, peut être, la question de ce qui est absolument nécessaire pour le maintient de la connexion!
A priori, je dirais qu'il lui faut:
Si ta classe ConnectionService a besoin que ta classe Network soit initialisée pour travailler, mais que ta classe Network a besoin d'une instance fonctionnelle de ConnectionService pour pouvoir être correctement initialisée, c'est qu'il y a, visiblement, un problème quelque part.
- une ip à contacter
- un login
- un mot de passe
- ET C'EST TOUT
Peut etre n'apparait-il que parce qu'il manque un mot dans la définition de ta classe Network, car, peut etre devrait elle être "fournir un mapping des serveurs actifs".
Tu te rendrais alors compte que, s'il est nécessaire, pour l'exécution de ton programme, d'avoir une liste de serveurs actifs, il est tout aussi nécessaire d'avoir une liste de serveur potentiels (comprend : que l'on peut essayer de contacter afin de mettre la connexion en place).
Cette liste de serveur potentiels pourrait venir de n'importe où, mais on peut envisager la simple utilisation d'un fichier ini (ou autre) qui listes les serveurs qui étaient connus à l'exécution précédente par exemple, ou l'utilisation de la base de registre pour les enregistrer.
Et tu pourrais meme, pourquoi pas, envisager de parcourir toutes les ip correspondant au réseau jusqu'à ce que tu en trouves une qui corresponde à un serveur actif, mais bon...
Ce qui importe, surtout, c'est de ne pas dépendre de ta classe Network (et de sa liste de serveurs) afin de pouvoir mettre ta connexion en place
Le seul moyen d'y arriver, c'est de fournir un liste d'ip que ton application peut essayer de contacter
En fonction de ton projet, de tes impératifs de sécurité, c'est à toi de voir quelle est la meilleure (ou du moins la moins mauvaise) solution pour arriver à fournir cette liste![]()
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
Etant donné que par réseau, j'entends réseau virtuel formé par tous les serveurs, je vais avoir du mal à tester toutes les ip d'internet
Pour avoir une liste de serveurs actifs (bien que je n'en vois pas l'intérêt) il suffit de faire une requête dans ce but sur le réseau et de compter les réponses ^^. J'aimerais éviter au possible d'avoir à connaitre tous les serveurs.
Et pour maintenir une connexion il suffit d'une seule chose : un sf::TcpSocket.
En fait je me demande si ConnexionService et Network ne sont pas une seule et même classe...
Tout ça est bien beau mais on s'éloigne du sujet
screetch donne un élément de réponse que je trouve très intéressant :
Je pense donc que je vais adapter ça à ma sauce, en modifiant quelque peu mon architecture (notamment en faisant un héritage multiple entre ConnexionService, Network, et Service).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 class Network { public: Network(sf::IpAddress join); Network(const Network&) = delete; virtual ~Network(); private: // ATTENTION, les initialisations sont faites dans l'ordre // de leurs declarations dans la classe, donc mettre le // champ modifiable en premier std::map<ServerId, std::unique_ptr<sf::TcpSocket>> m_otherServers; const ServerId m_id = 1; static ServerId doConnectToIP(std::map& serverList, sf::IpAddress join); }; Network::Network(sf::IpAddress join) : m_otherServers() , m_id(doConnectToIP(m_otherServers, join) { }
Partager