Ton raisonnement me parait viable (même si je trouve que l'implémentation de bin_int laisse à désirer).
Ton raisonnement me parait viable (même si je trouve que l'implémentation de bin_int laisse à désirer).
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Si c'était aussi simple, on pourrait faire des égalités entre flottants sans se préoccuper de l'epsilon... Or, ce n'est pas le cas.
Le problème de ta sérialisation, c'est qu'elle est bien plus complexe que la redéfinition d'un opérateur de comparaison sur la structure complète, en utilisant donc les champs de façon native.
Et c'est faux pour la plupart des autres types, pour ne pas dire "tous". Je te cite l'extrait de l'aide de memcmp :C'est à dire que memcmp retourne le résultat de comparaison dès la première différence rencontrée. Il ne retourne zéro (égalité) qu'après parcours intégral des N octets à comparer.Envoyé par man memcmp
Donc, pour comparer par exemple deux flottants, tu vas devoir partir des principes suivants :
- Normalisation du flottant sous forme ASCII, avec tous les zéros non-significatifs. Cela doit représenter facilement 300 ou 400 chiffres, je pense.
- Prier pour que la précision inhérente aux flottants ne perturbe pas ladite représentation ASCII (et c'est déjà mal barré...).
- En cas d'égalité, ton memcmp sera "juste" cent fois plus lent qu'une comparaison directe de float à float. En cas d'inégalité, tu ne gagneras pas spécialement beaucoup plus de temps.
De plus, ta sérialisation devra être stockée en mémoire, et à force ça va être coûteux en temps machine comme en place utilisée si tu effectues une sérialisation complète permettant un ordre total.
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
La phrase clé ici étant "moyennant une sérialization binaire ad-hoc".
Ben non: Il suffit de sérialiser l'exposant avant la mantisse. Ainsi, la mantisse ne sera comparée que si les exposants sont égaux, supprimant ainsi la nécessité de normalisation.
De plus, il faut également sérialiser le signe avant l'exposant.
En clair, si tu sérialises en big-endian un flottant IEEE, tu as tout ça de déjà fait pour toi (sauf peut-être pour les infinis et NaNs).
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Oui, et alors ?
Dans mon propos je souhaite comparer des flux binaires construits dans une même séquence donnée (fallait-il le préciser, c'est quand même évident, non ?). Il n'est pas question de comparer des int avec des float, mais des int avec des int, des float avec des float, etc, avec, dans tous les cas, la même fonction de comparaison (et memcmp() me semble convenir).
Ouf ! L'honeur est sauf. Mon raisonnement n'est pas faux, c'est déjà ça
Mais bon, le code brut proposé n'est là que pour illustrer le concept que j'essaye de mettre en place. Voici déjà une toute petite évolution qui ajoute les notions ASC et DESC (comme en SQL):
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 struct bin_int { union { int i; char bin[sizeof(int)]; }; bin_int(int _i):i(_i) {} bin_int & ascending() { i-=INT_MIN; std::swap(bin[0],bin[3]); std::swap(bin[1],bin[2]); return *this; } bin_int & descending() { i-=INT_MIN; i=UINT_MAX-i; std::swap(bin[0],bin[3]); std::swap(bin[1],bin[2]); return *this; } unsigned size() const { return sizeof(int); } };
Franchement, j'ai du mal à voir comment tu comptes utiliser ascending et descending ici.
De plus, pour moi, tu ne devrais avoir qu'une seule valeur: les quatre octets.
En fait, j'imagine un truc de ce genre:
Code C++ : 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 //Pour ntohs()/ntohl() #ifdef _WIN32 #include <winsock2.h> #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif #else #include <arpa/inet.h> #endif class be_int { private: char bytes[sizeof(int)]; public: void SetBE(int beVal) { *reinterpret_cast< int* >(bytes) = beVal; } int GetBE() const { int beVal = *reinterpret_cast< int const* >(bytes); return beVal; } void SetHost(int hostVal) { SetBE(htonl(hostVal)); } int GetHost() const { return ntohl(GetBE()); } byte const *GetBytes() const { return bytes;} static size_t SGetSize() { return sizeof bytes; } be_int(int hostVal) { SetHost(hostVal); } };
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
C'est à dire (exprimé différemment) "moyennant une tripaille absolument pas portable et totalement dépendante de l'implémentation"... Mouais... Je préfère gérer un opérateur, pour ma part.
Je cite :Donc, déjà, va falloir faire les cas pour chaque type de flottant, découper à des valeurs différentes des mots-machine (23 bits, 52 bits, etc..), inverser le bit de signe (ben oui : 1=négatif, 0=positif => logique inverse), tenir compte du décalage de l'exposant, et enfin gérer tout ça avec une endianness différente de la plus courante (little-endian sur PC, pour mémoire). Je passe sur le problème de conversion / normalisation des flottants invalides, mais qui est malgré tout un vrai problème.Envoyé par Format IEEE-754
Je suis le seul à trouver que faire un opérateur sur la structure, qui sera en plus portable, sera bien plus simple, plus rapide et plus efficace que cette usine à gaz potentielle ???
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
+1 avec Mac LAK
+1 aussi, ca me parait bancal et pas sûr
disons que sur le papier, ca marche, mais que l'implémentation risque d'être illisible sans commentaires de partout, dangereuse et très difficile a débugger. lorsqu'elle se trompera dans la comparaison, tu pourrais passer des jours a trouver le problème
plus le fait que tu vas avoir besoin d'une implémentation différente pour chaque type, ce qui va te faire ecrire pas mal de code au final.
+1, mais surtout je vois mal où est la lourdeur d'un op< redéfini par rapport à l'usine à gaz que ça va être là...
"Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu
Les "pluzun" c'est pour dire que quelques petits décalages de bit font peur ?
La discussion a quelque peu dérivé sur les float alors qu'ils ne sont pas importants sur le fond.
Mon orgueil en a pris un coup suite à vos interventions, et pourtant j'ai le sentiment que vous n'avez pas compris le concept que j'ai essayé d'exprimer dans le premier post de cette discussion. J'ai sans doute du mal à faire passer mon message
J'ai donc retroussé mes manches et j'ai repris le problème depuis le début. Pas de babla, du code.Usine à gaz, non portable, illisible... rien de tout ça me semble-t-il, et je répond à ce je souhaitais au départ: sérializer une clé de manière descriptive et non pas procédurale. Une sorte de mix entre SQL et boost::serialize en somme.
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229 #include "tchar.h" #include <climits> #include <iostream> #include <iomanip> #include <sstream> #include <functional> #include <vector> #include <set> #include <algorithm> struct endian_key { std::string s; char *buffer(size_t size) { size_t l=s.length(); s.resize(s.length()+size); std::string::iterator b=s.begin(); std::advance(b,l); return &*b; } template<typename T> endian_key & asc(T const & v) { ascending(*this,v); return *this; } template<typename T> endian_key & desc(T const & v) { descending(*this,v); return *this; } endian_key & asc(int v); endian_key & desc(int v); endian_key & asc(unsigned v); endian_key & desc(unsigned v); }; struct endian_key_pred { int index; endian_key_pred(int i):index(i) {} template<typename T> bool operator()(T const & v1,T const & v2) const { endian_key sk1,sk2; v1.make_key(sk1,index); v2.make_key(sk2,index); return ::memcmp(sk1.s.c_str(),sk2.s.c_str(),std::min(sk1.s.length(),sk2.s.length())) < 0; } }; //struct bin_int_trait //{ // typedef int type; inline void ascending(endian_key & sk,int i) { char *ptr=sk.buffer(sizeof(int)); i-=INT_MIN; char *bin=(char *)&i; std::swap(bin[0],bin[3]); std::swap(bin[1],bin[2]); memcpy(ptr,bin,sizeof(int)); } inline void descending(endian_key & sk,int i) { char *ptr=sk.buffer(sizeof(int)); i-=INT_MIN; i=UINT_MAX-i; char *bin=(char *)&i; std::swap(bin[0],bin[3]); std::swap(bin[1],bin[2]); memcpy(ptr,bin,sizeof(int)); } //}; inline endian_key & endian_key::asc(int v) { ascending(*this,v); return *this; } inline endian_key & endian_key::desc(int v) { descending(*this,v); return *this; } inline void ascending(endian_key & sk,unsigned u) { char *ptr=sk.buffer(sizeof(unsigned)); char *bin=(char *)&u; std::swap(bin[0],bin[3]); std::swap(bin[1],bin[2]); memcpy(ptr,bin,sizeof(unsigned)); } inline void descending(endian_key & sk,unsigned u) { char *ptr=sk.buffer(sizeof(unsigned)); u=UINT_MAX-u; char *bin=(char *)&u; std::swap(bin[0],bin[3]); std::swap(bin[1],bin[2]); memcpy(ptr,bin,sizeof(unsigned)); } inline endian_key & endian_key::asc(unsigned v) { ascending(*this,v); return *this; } inline endian_key & endian_key::desc(unsigned v) { descending(*this,v); return *this; } inline void ascending(endian_key & sk,std::string const & s) { size_t len=s.length()+1; char *ptr=sk.buffer(len); ::memcpy(ptr,s.c_str(),len); } inline void descending(endian_key & sk,std::string const & s) { size_t len=s.length()+1; char *ptr=sk.buffer(len); ::memcpy(ptr,s.c_str(),len); unsigned char *uptr=reinterpret_cast<unsigned char *>(ptr); for(;*uptr;++uptr) *uptr=UCHAR_MAX-*uptr; *uptr=UCHAR_MAX; } // c'est à partir d'ici qu'on bénéficie du brol qui précède struct MyRecord { int m_i; std::string m_s; void make_key(endian_key & sk,int index) const { switch (index) { case 0: // syntaxe qui fait penser à "CREATE INDEX idx_0 ON MyRecord (m_i ASC, m_s DESC)" sk.asc(m_i).desc(m_s); case 1: // syntaxe qui fait penser à "CREATE INDEX idx_1 ON MyRecord (m_s ASC)" sk.asc(m_s); }; } //operator<(), à la "mode" STL friend bool operator<(MyRecord const & v1,MyRecord const & v2) { return v1.m_s < v2.m_s; //note: l'operator<() entre deux std::string fait appel à... memcmp() ! //fichier include <iosfwd> ligne 436 de VS2005 } }; int _tmain(int argc, _TCHAR* argv[]) { std::vector<MyRecord> vec; MyRecord r; r.m_i=-7; r.m_s="roej"; vec.push_back(r); r.m_i=4; r.m_s="ane"; vec.push_back(r); r.m_i=-7; r.m_s="zie"; vec.push_back(r); r.m_i=1; r.m_s="tup"; vec.push_back(r); r.m_i=4; r.m_s="bobar"; vec.push_back(r); r.m_i=-7; r.m_s="tup"; vec.push_back(r); r.m_i=1; r.m_s="axyze"; vec.push_back(r); r.m_i=-7; r.m_s="taron"; vec.push_back(r); //tri suivant l'index 0 std::sort(vec.begin(),vec.end(),endian_key_pred(0)); for (std::vector<MyRecord>::iterator it=vec.begin();it!=vec.end();++it) std::wcout << it->m_i << " - " << it->m_s.c_str() << std::endl; std::wcout << std::endl; //tri suivant l'index 1 std::sort(vec.begin(),vec.end(),endian_key_pred(1)); for (std::vector<MyRecord>::iterator it=vec.begin();it!=vec.end();++it) std::wcout << it->m_i << " - " << it->m_s.c_str() << std::endl; std::wcout << std::endl; //retri suivant l'index 0 std::sort(vec.begin(),vec.end(),endian_key_pred(0)); for (std::vector<MyRecord>::iterator it=vec.begin();it!=vec.end();++it) std::wcout << it->m_i << " - " << it->m_s.c_str() << std::endl; std::wcout << std::endl; //tri suivant operator<(), équivalent au tri suivant l'index 1 std::sort(vec.begin(),vec.end()); for (std::vector<MyRecord>::iterator it=vec.begin();it!=vec.end();++it) std::wcout << it->m_i << " - " << it->m_s.c_str() << std::endl; std::wcout << std::endl; //un autre conteneur std::set<MyRecord,endian_key_pred> tree(vec.begin(),vec.end(),endian_key_pred(1)); for (std::set<MyRecord,endian_key_pred>::iterator it=tree.begin();it!=tree.end();++it) std::wcout << it->m_i << " - " << it->m_s.c_str() << std::endl; std::wcout << std::endl; return 0; }
3 étapes dans ce code.
- Une structure de serialisation (endian_key) avec le code explicite de trois types de base: int, unsigned et std::string. La paire de fonction ascending() et descending() est conceptuellement semblable à l'operator<<() des iostream associés aux manipulateurs (setw, setprec, setbase, etc).
- Un exemple de structure MyRecord dans laquelle les clés à sérialiser sont "décrites". Je trouve que c'est très explicite et lisible. Pour montrer l'analogie que je viens de faire avec les iostream, à la place de sk.asc(m_i).desc(m_s); on aurait pu imaginer une syntaxe comme sk << asc() << m_i << desc() << m_s; par exemple.
- Un exemple de code montrant l'usage des clés par l'intermédiaire de deux conteneurs. A noter l'usage d'un seul et unique prédicat indépendant de la structure (endian_key_pred).
Ceci n'est que le premier jet concret d'une reflexion. Le mérite est que ça fonctionne et que ça me permet d'avoir une base appliquée pour la suite: quid des performances, est-ce une bonne approche conceptuel, est-ce utile, etc ?
De toutes façons, en tant que simple défi à écrire du C++ (du vrai ! ) c'est déjà une réussite
J'espère que tu vois maintenant
C'est toujours un bordel monstre et j'y comprends toujours rien.
Tu es toujours incapable d'utiliser (ou au moins reproduire) un vrai htonl()/ntohl(), ton code dépend toujours de l'endianness...
De plus, à ma connaissance on n'est pas supposé pouvoir obtenir un pointeur non-const sur l'intérieur du buffer d'une string...
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Comme je te l'ai déjà dit, c'est une "usine à gaz" parce que c'est excessivement complexe pour, au final, une simple comparaison d'entiers et de chaînes !
Côté performances, vu le nombre de swaps (forcément non-alignés, donc coûteux) et de traitements de copie que tu fais subir à tes données, ça m'étonnerait beaucoup que tu puisses obtenir des performances fulgurantes sur de gros volumes de données...
Cela reste un avis personnel, mais quand on se sent "obligé" de se calquer sur un principe existant, mais "étranger" au langage que l'on utilise, c'est que l'on a fait une erreur de conception : soit en prenant le mauvais langage à la base, soit parce que l'on n'a pas su trouver la méthode "native" adéquate.
En exagérant à peine, cela me fait penser aux plaintes sur le C++ comme quoi il ne possède pas l'équivalent de la fonction "eval" si chère aux langages interprétés : cela montre que l'on a des raisonnements qui restent ancrés dans un "autre monde" et qui ne sont pas forcément adaptés au langage manipulé.
En guide de boutade :Pour ma part, je bosse dans l'embarqué temps réel, je "bouffe" des décalages de bits, des registres, des contrôleurs et des protocoles réseau à longueur de temps. Donc, non, ce ne sont pas quelques opérations dépendantes de la plate-forme matérielle qui me gênent, loin de là. C'est plutôt que je n'en vois pas l'utilité à un si haut niveau de code, où c'est plus nuisible qu'autre chose.
La preuve : tu n'as pas une seule ligne de gestion de l'endianness courante de la machine. Ton code ne fonctionnerait pas sur un processeur big-endian, tout comme il est presque certain qu'il ne fonctionnerait pas non plus sur une machine 64 bits. Rajouter cette gestion alourdirait énormément ton code, le rendant donc encore plus volumineux et donc encore moins performant... Vois-tu mieux pourquoi je dis que c'est une usine ?
Après, soyons bien clairs : je ne t'en veux pas personnellement, non je n'ai pas juré de descendre ton code, ni quoi que ce soit d'autre à ton encontre.
C'est juste que ton code semble être une brique primaire de ton projet. Or, quand on commence à faire des usines ou des mikados sur les briques élémentaires d'un projet, à la fin, ça fait surtout une gigantesque masse de spaghettis mêlée de poulpes amoureux. Cela devient totalement inmaintenable. C'est une situation que j'ai déjà vécue, et franchement, je n'ai pas apprécié des masses le démêlage de la tripaille.
Je cherche juste à te faire comprendre ça pour t'éviter une erreur qui aura des conséquences plus ou moins fâcheuses.
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
Je comprend qu'on puisse ne pas comprendre
C'est du détail d'implémentation. C'est bien sûr à revoir, le code que j'ai posté en exemple n'est justement qu'un... exemple. Ca permet pour l'instant de "visualiser" le concept.
Ceci dit, je suis preneur d'une version inline et/ou asm pour x86/x64 de htonl()/ntohl() et htons()/ntohs() (et pareil pour les mots en 64bits). Pour les autres processeurs on verra dans une autre vie...
Encore une fois, c'est du détail d'implémentation. C'est vrai que j'aurais pu utiliser un std::vector<char> par exemple, mais le string est tellement facile. Et puis en pratique, même si ce n'est pas "officiel", je pense bien que toute les implémentations de string utilisent un buffer continu en mémoire.
Traditionnellement, j'utilise un truc de ce genre:
C'est indépendant de l'endianness.
Code C++ : 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 inline unsigned short my_htons(unsigned short u) { unsigned char b[2] = { static_cast<unsigned char>(u >> 8), static_cast<unsigned char>(u) }; return *reinterpret_cast<unsigned short*>(b); } inline unsigned long my_htonl(unsigned long u) { unsigned char b[4] = { static_cast<unsigned char>(u >> 24), static_cast<unsigned char>(u >> 16), static_cast<unsigned char>(u >> 8), static_cast<unsigned char>(u) }; return *reinterpret_cast<unsigned long*>(b); }
Pardon, lapsus. Je voulais dire un pointeur non-const.Encore une fois, c'est du détail d'implémentation. C'est vrai que j'aurais pu utiliser un std::vector<char> par exemple, mais le string est tellement facile. Et puis en pratique, même si ce n'est pas "officiel", je pense bien que toute les implémentations de string utilisent un buffer continu en mémoire.
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
(c'est aussi, accessoirement, assez lent)
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
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