IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Petite aide à la sérialisation


Sujet :

C++

  1. #1
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut Petite aide à la sérialisation
    Voici le genre de chose que je faisais et fais encore
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #pragma pack(1)
    struct record
    {
    char name[20];
    unsigned id,seq;
    };
     
    record r;
    strcpy(r.name,s.c_str());// bof bof...
    r.id=25;
    r.seq=10;
    ...
    file.write(&r,sizeof(record));
    A la longue c'est fastidieux et on se retrouve avec des dizaines si pas des centaines de structures en tout genre avec à chaque fois des kilomètres de code pas très fiable associés à chaque structure. Bref, c'est du C, pas vraiment du C++.

    J'ai vu un peu comment était construit boost::serialize avec la méthode intrusive serialize(). Le principe est très sympa.
    J'aimerais m'en inspirer pour généraliser quelque chose de ce genre:
    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
    struct record
    {
    std::string name;
    unsigned id,seq;
     
    void serialize_record(archive_record & a)
    {
     a & name & id;
    }
     
    void serialize_keysort(archive_keysort & a,int keynumber)
    {
     switch (keynumber)
     {
      case 0: a & id; break;
      case 1: a & name & seq; break;
     }
    }
     
    void serialize_keysearch(archive_keysearch & a,int keynumber)
    {
     switch (keynumber)
     {
      case 0: a & id; break;
      case 1: a & name; break;
     }
    }
    };
    En gros, j'ai des records à stocker avec une ou plusieurs clé de tri (et donc de recherche). Dans l'exemple j'ai "name" et "id" qui sont des infos utiles à stocker, et les deux participent chacune dans une clé de tri. Au delà, il y a aussi "seq" mais qui n'est utile que pour imposer une sequencement ordonné dans une des clés, après je n'en ai plus besoin.

    Un petite aide pour poursuivre ma réflexion serait bienvenue. Et surtout s'il existe déjà du tout fait, c'est encore mieux
    Merci.

  2. #2
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Bon ben il semble que cela n'inspire personne
    Vous ne faites jamais de la gestion de fichiers un peu élaborée, ou de bd "custom" ?

    Bah, j'ai un peu simplifié mon modéle de réflexion. Mon truc avec les clés est un peu compliqué, j'ai simplifié en ceci pour n'avoir qu'une clé par structure:
    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
    struct record
    {
    std::string name;
    unsigned id;
     
    template <class archive_record>
    void serialize_record(archive_record & a)
    {
     a & name & id;
    }
     
    template <class archive_key>
    void serialize_key(archive_key & a)
    {
      a & id;
    }
    };

  3. #3
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    En général, vu le niveau auquel je travaille (bas niveau), j'applique le plus souvent deux choses totalement disjointes :
    • Je sérialise "brutalement" un attribut de ma classe, qui est la plupart du temps une structure packed servant de container interne à la classe, via la surcharge de "<<" et ">>" sur la classe elle-même, ou carrément via des fread/fwrite (je sais, c'est mal, m'en fous). Ça, c'est pour ce qui tourne tout le temps, genre stockage brutal, communications réseau intensives, etc.
    • Si c'est de la configuration initiale, j'utilise plutôt de l'XML avec des wrappers automatiques depuis le schéma XSD, qui valide au maximum possible les fichiers XML afin de m'éviter d'avoir à le faire au niveau applicatif.
    Si ça peut t'aider...
    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

  4. #4
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    OK, merci.

    Je vais me débrouiller et je ferai part de mes résutats si ça intéresse quelqu'un

    J'aimerais cependant un petite aide pour sérializer en binaire tous les types courants de sorte que
    memcmp(v1,v2,min(v1.length(),v2.length()))
    renvoit la bonne valeur quoiqu'il y ait dans v1 et v2. Par exemple:
    v1 << int(5) << std::wstring(L"abc") << float(23.7).
    v2 << int(-7) << std::wstring(L"xyzt") << float(-0.095).
    je suppose que ce n'est qu'un problème d'ordre "endian" et rien d'autre.

  5. #5
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Quant on sérialise, par définition, on DOIT tenir compte de l'endianness et de l'alignement si l'on désire avoir quelque chose de transmissible à une autre machine.

    Dans ton cas, il faut bien sûr s'assurer de la taille des données, de l'endianness (little-endian sur un PC, mais big-endian sur un réseau en général), et du padding entre les divers éléments quand ils sont en mémoire.

    Sur ton exemple, comme les chaînes de caractère sont de taille différente, ça n'aura jamais de sens : le champ flottant ne sera aligné avec celui d'une autre structure que par miracle. Si tu veux éviter ça, il faut utiliser une taille constante sur les chaînes, et remplir la partie non-utilisée de zéros. Par contre, tu vas perdre de la place, forcément...
    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

  6. #6
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Sur ton exemple, comme les chaînes de caractère sont de taille différente, ça n'aura jamais de sens : le champ flottant ne sera aligné avec celui d'une autre structure que par miracle. Si tu veux éviter ça, il faut utiliser une taille constante sur les chaînes, et remplir la partie non-utilisée de zéros. Par contre, tu vas perdre de la place, forcément...
    Tu peux aussi préfixer la chaîne de caractère par sa taille. Ca permet de perdre moins de place, par contre, ça t'oblige à décoder la structure séquentiellement (plus possible d'accéder directement à un champ sans avoir lu tous les précédents).

  7. #7
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Tu peux aussi préfixer la chaîne de caractère par sa taille. Ca permet de perdre moins de place, par contre, ça t'oblige à décoder la structure séquentiellement (plus possible d'accéder directement à un champ sans avoir lu tous les précédents).
    D'un autre côté, on est dans une sérialisation, donc séquentielle...
    Le fait de terminer les chaînes par un zéro terminal marche aussi, bien entendu.
    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

  8. #8
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Les chaines de caractères seront terminées par zéro, cela devrait permettre de comparer des flux binaire de longueur quelconque avec du texte en milieu de flux (pas besoin d'aligner les champs donc).
    Cependant, il faut aussi sur PC respecter le bon "endian":
    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
    #include "tchar.h"
    #include <iostream>
    #include <sstream>
    #include <functional>
     
    template <typename T>
    bool cmp(T v1,T v2)
    {
    	return memcmp(&v1,&v2,sizeof(T))<0;
    }
     
    int _tmain(int argc, _TCHAR* argv[])
    {
    	std::wcout << std::less<int>()(-10,1000000000) << std::endl;
    	std::wcout << cmp(-10,1000000000) << std::endl;
    	return 0;
    }
    Le résultat devrait être le même, mais il ne l'est pas. Résultat
    1
    0

  9. #9
    screetch
    Invité(e)
    Par défaut
    ca n'effectue pas la meme opération...
    memcmp compare la mémoire octet par octet
    or, le premier bit de -10 (le signe) fait que traité en memoire brute, le premier octet est supérieur au second (qui est un entier positif)

  10. #10
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Il me semblait bien...
    Citation Envoyé par camboui Voir le message
    je suppose que ce n'est qu'un problème d'ordre "endian" et rien d'autre.
    L'affirmation ci-dessus de mon précédant post est donc fausse

    Je repose donc la question:
    comment sérializer en binaire tous les types courants de sorte que
    memcmp(v1.void_ptr(),v2.void_ptr(),min(v1.length(),v2.length()))
    renvoit la bonne valeur quoiqu'il y ait dans v1 et v2 ?

  11. #11
    screetch
    Invité(e)
    Par défaut
    ca dépend de ce que tu veux tester. Si tu veux tester l'égalité, le memcmp fonctionne
    si tu veux tester a < b quel que soit le type de a et b ca ne marchera jamais, puisque ca dépend de la représentation binaire du type.

  12. #12
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    C'est mort à mon avis. memcpy compare de la mémoire, c'est tout. Pour une comparaison sémantique, il faut des types et l'opérateur <.

    Donc, étape suivant, on remonte au besoin. Que cherches-tu à faire exactement ?

  13. #13
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Pareil : memcmp, c'est pour l'égalité exacte. Pour des opérations de comparaison avec gestion d'un epsilon (flottants), gestion de l'ordre lexicographique, sensibilité ou non à la casse, et comparaison générale, il faudra redéfinir les opérateurs ">" et "<" pour cette structure.
    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

  14. #14
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Je cherche à sérialiser en binaire de sorte à pouvoir comparer avec memcmp(). Les flux binaires seront stockés sur fichier et doivent être indépendant du langage de programmation et de la platforme.

  15. #15
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Je cherche à sérialiser en binaire de sorte à pouvoir comparer avec memcmp().
    On remonte d'un cran. Pourquoi tiens-tu à comparer avec memcmp des choses qui ne sont pas comparables avec memcmp (memcmp n'a pas de notion de sémantique, elle ne saura jamais faire la différence entre un entier signé et un entier non signé, par exemple). Ce n'est pas un besoin, ça, c'est au pire une contrainte, et si tu peux la faire sauter d'une manière ou d'une autre, tu t'éviteras bien des tracas.

    Les flux binaires seront stockés sur fichier et doivent être indépendant du langage de programmation et de la platforme.
    Ca ce n'est pas un soucis. Tu as déjà les éléments là-dessus.

  16. #16
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Il n'y a pas besoin de notion sémantique pour comparer deux flux binaires.
    J'essayerai d'expliquer plus tard le "besoin" de se passer de décrire l'operator<() à tout bout de champ.

    Pour l'instant, je souhaite simplement une aide technique. Voici l'exemple un peu modifié qui convertit tout entier signé ou non en un flux binaire de sorte à pouvoir comparer avec memcmp():
    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
    template <typename T>
    bool cmp(T v1,T v2)
    {
    	return memcmp(&v1,&v2,sizeof(T))<0;
    }
     
    struct bin_int
    {
    	union
    	{
    		int i;
    		char bin[sizeof(int)];
    	};
    	bin_int(int _i):i(_i) {}
    	bin_int tobin()
    	{
    		i+=65536*32768;
    		std::swap(bin[0],bin[3]);
    		std::swap(bin[1],bin[2]);
    		return *this;
    	}
    };
     
    struct bin_uint
    {
    	union
    	{
    		unsigned u;
    		char bin[sizeof(unsigned)];
    	};
    	bin_uint(unsigned _u):u(_u) {}
    	bin_uint tobin()
    	{
    		std::swap(bin[0],bin[3]);
    		std::swap(bin[1],bin[2]);
    		return *this;
    	}
    };
     
     
    int _tmain(int argc, _TCHAR* argv[])
    {
    	std::wcout << sizeof(bin_int) << std::endl;
    	std::wcout << std::less<int>()(-10,1000000000) << std::endl;
    	std::wcout << cmp(-10,1000000000) << std::endl;
    	std::wcout << cmp(bin_int(-10).tobin(),bin_int(1000000000).tobin()) << std::endl;
    	return 0;
    }
    Comparer des int et des unsigned avec memcmp() par l'intermédiare de bin_int et bin_uint fonctionnera toujours.

  17. #17
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Comparer des int et des unsigned avec memcmp() par l'intermédiare de bin_int et bin_uint fonctionnera toujours.
    Je te souhaite bien du courage pour faire la même chose sur les flottants... Et encore pire sur n'importe quel type un peu complexe (d'ailleurs, oui, la notion d'infériorité sur les complexes, c'est toujours amusant).

    Pour moi, si tu ne remets pas en question l'approche, tu vas dans un mur.

  18. #18
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    +1 : comparer des entiers "normalisés" (endianness et taille forcée notamment), c'est jouable avec memcmp.
    Avec des chaînes, c'est déjà moins marrant, et avec des flottants ou tout autre type "évolué", c'est le mur garanti.

    Pourquoi veux-tu absolument utiliser memcmp ? Surtout en C++ ?
    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

  19. #19
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Et pourquoi les flottants float/double poseraient problème ? Ne sont-ils pas normalisés par une directive IEEE machin-chose ?
    Il suffit de faire comme si on avait deux entiers consécutifs, le premier serait l'exposant, le second la mantisse.
    Si j'ai bon souvenir, l'exposant d'un double est codé sur 1 byte, la mantisse sur 7. Pour le float je ne sais plus. Mais dans les deux cas une manip dans le genre de mon exemple bin_int::dobin() est tout à fait faisable.

    J'émet le postulat suivant:
    toute structure ordonnable par operator<() le sera par memcmp() moyennant une sérialization binaire ad-hoc.
    Je l'ai prouvé pour int et unsigned (c'est semblable pour toutes les tailles d'entiers sur 2, 4, 8 octets, etc).

    Avec les std::string c'est trivial:
    memcmp(s1.c_str(),s2.c_str(),min(s1.length(),s2.length())+1)
    Notez que le zero terminal doit être compris dans le flux binaire.

    Avec les std::wstring c'est similaire, sauf qu'il faut en plus mettre chaque wchar_t dans le bon ordre endian.
    Les problèmes de case éventuel sont à gérer en amont.

    Les complexes dites-vous ? Ils sont ordonnables via operator<() ? Alors ils le sont via un flux binaire semblable aux précédants cas.

    Je rappelle que memcmp(p1,p2,n) compare n octets au maximum, elle s'arrête au premier octet différent. n doit être le minimum des longueurs de p1 et p2.

    Je rappelle aussi que je souhaite sérialiser deux choses bien distinctes:
    -une structure (un peu comme boost::serialize);
    -une clé associée, utilisable par une fonction de comparaison "universelle" (il ne s'agit pas de reconstruire la structure à partir de cette clé).

    En conclusion, je souhaite juste pouvoir construire des clés qui ne seraient que des combinaisons d'entier, de string, et éventuellement de float. Je voudrais décrire la clé mais ne pas la coder, une decription à la SQL: CREATE INDEX index ON table (field1,field2,field3).

    Si vous n'aimez pas memcmp(), c'est ok, mais alors dites-moi comment juste décrire une clé assicoée à une structure sans devoir la coder implicitement dans un fastidieux operator<() ?
    J'espère que vous me comprenez maintenant

  20. #20
    screetch
    Invité(e)
    Par défaut
    tu pourrais avoir l'égalité binaire entre un flottant et un entier sans que les deux soient égaux (un représente 1.0 et l'autre 1069547520)

Discussions similaires

  1. petite aide sur une requete INSERT INTO
    Par bonneti dans le forum Langage SQL
    Réponses: 3
    Dernier message: 14/03/2005, 15h17
  2. petite aide sur les transactions et triggers SVP
    Par CharleLéo dans le forum Débuter
    Réponses: 4
    Dernier message: 15/11/2004, 20h43
  3. Petite aide pour gros problème : libstdc++
    Par Info-Rital dans le forum Linux
    Réponses: 5
    Dernier message: 30/08/2004, 19h17
  4. Petite aide sur les triggers ?
    Par krimson dans le forum PostgreSQL
    Réponses: 3
    Dernier message: 16/04/2004, 16h28
  5. Une petite aide pour les API ?
    Par Yop dans le forum Windows
    Réponses: 2
    Dernier message: 04/04/2002, 21h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo