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 :

Fonctions multi types


Sujet :

C++

  1. #21
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par peufeu Voir le message
    Je préfère te mettre un exemple (en pièce jointe) de métaprog/réflexion particulièrement couillue (sqlite + SQLAlchemy + Elixir) ... et ici totalement hors sujet puisqu'on parle de C++. Note qu'on doit quand même spécifier la liste des champs puisqu'on a une table SQLite... mais pour une sérialisation transparente y'a pickle ou serialize en php...
    Si ce n'est que, lorsqu'il s'agit de sérialiser des types primitifs ou des chaines de caractères, il n'y a jamais de problème...

    Mais, dés qu'il s'agit de sérialiser des types complexes, tu dois, quoi qu'il arrive, sérialiser tous les membres (sans doute de manière récursive si, d'aventure, un des membre vient à être une classe ou une structure)
    boost.serialize a l'air beaucoup plus digeste que la solution "à la pougne" ceci dit.
    Tout à fait
    Par exemple à créer une interface ISerializable avec les méthodes idoines de façon à définir Fichier << objet qui appelle objet->write( Fichier ) pour bénéficier de l'appel de la virtual sur la fonction de sérialisation write()...
    C'est exactement ce que l'on fait en C++, non

    Il faut bien comprendre que, de manière générale, une fonction qui intervient dans un contexte d'interface devra, quoi qu'il arrive, être spécialisée / redéfinie pour s'adapter au type dynamique au départ duquel elle est invoquée... Il en va strictement de même en C++.

    les opérateurs de flux sont à utiliser lorsque tu n'a pas d'héritage, ou à définir sur base d'une fonction virtuelle (éventuellement pure dans la classe de base), qui devra être redéfinie / spécialisée pour les classes dérivées.

    Au final, tu aura toujours à peu près autant d'instructions à (re) copier que de membre à sérialiser

    Perso je suis devenu de plus en plus allergique aux formats de fichier codés à l'arrache, l'écriture du parser prend en général beaucoup trop de temps XD
    Nous sommes bien d'accord là dessus, mais il faut aussi dire que les alternatives nécessitent, au minimum, de connaitre un tout petit peu la conception et le langage.

    Il est clair que boost.serialize (par exemple) sera surement bien plus facile à l'emploi et plus rapide à la mise en oeuvre.

    Mais quant on voit que fanfouer part déjà sur un héritage qui n'a pas lieu d'être et le niveau relativement "basique" de sa question, on se dit qu'il vaut peut être mieux commencer par lui "inculquer les bases" de la POO (de manière générale) et de C++ (en particulier) avant de commencer à lui parler de boost et des solutions complexes à comprendre car basées essentiellement sur la programmation générique

    Si, déjà, on peut l'inciter à utiliser std::string std::vector et autres std::, de sera pas si mal...

    Une fois que nous aurons "rectifié le tir" sur les bases, nous pourrons envisager d'aller "plus loin"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main()
    {
            std::string machaine( "1 est < à 2" );
            std::ofstream ofs("test.txt");
            ofs     << "<Objects>"
                    << std::endl
                    << "\t<item id=\"1\">"
                    << machaine
                    << "</item>"
                    << std::endl
                    << "</objects>"
                    << std::endl;
            return 0;
    }
    Là le fichier XML n'est pas valide puisqu'il contient un < qui traîne... il faut échapper ...
    non pas échapper... convertir, en entité html (&lt ou similaire.

    Typiquement, il s'agirait de créer ne fonction qui effectue ce genre de traitement pour l'ensemble des caractères susceptibles de rendre une partie de fichier invalide.

    Mais ce n'est pas "échapper" qu'il faut faire
    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

  2. #22
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 70
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Si ce n'est que, lorsqu'il s'agit de sérialiser des types primitifs ou des chaines de caractères, il n'y a jamais de problème...

    Mais, dés qu'il s'agit de sérialiser des types complexes, tu dois, quoi qu'il arrive, sérialiser tous les membres (sans doute de manière récursive si, d'aventure, un des membre vient à être une classe ou une structure)
    Ouais. Malheureusement sans réflexion, pas de fonction serialize() automatique...

    Pour le cas qui nous intéresse (un genre d'émulation de BDD) ce serait étonnant d'en arriver là cependant, enfin ça dépend si il y a des relations...

    Citation Envoyé par koala01 Voir le message
    C'est exactement ce que l'on fait en C++, non
    En fait, je parlais de C++ là XD

    Citation Envoyé par koala01 Voir le message
    Il faut bien comprendre que, de manière générale, une fonction qui intervient dans un contexte d'interface devra, quoi qu'il arrive, être spécialisée / redéfinie pour s'adapter au type dynamique au départ duquel elle est invoquée... Il en va strictement de même en C++.
    Pas forcément en fait, par exemple tu peux avoir une classe qui implémente l'opérateur cmp(a,b) (qui retourne 0,1,-1 comme strcmp), et tu rajoutes un mixin (en C++ un héritage multiple) qui définit < > =. Ou ta classe implémente une interface de tableau ([] et len()) et tu ajoutes un mixin qui créé les itérateurs et autres cerises sur le gâteau... donc héritage multiple de code autant que d'interface, souvent utilisé en Ruby d'ailleurs...

    Citation Envoyé par koala01 Voir le message
    les opérateurs de flux sont à utiliser lorsque tu n'a pas d'héritage, ou à définir sur base d'une fonction virtuelle (éventuellement pure dans la classe de base), qui devra être redéfinie / spécialisée pour les classes dérivées.
    Faut faire attention aux opérateurs de flux, si tes strings contiennent des espaces ou autre caractères significatifs (comme le < en XML). Je préfère prendre un module de parser tout fait qui gère tous les cas...

    A la limite pour l'OP si un fichier texte "flatfile" est choisi, un parseur de CSV pourrait être intéressant, le fait de pouvoir charger le truc dans un tableur après peut être un bonus :

    http://stackoverflow.com/questions/1...sv-parser-in-c

    Ou du XML (plus verbeux à parser) ou du YAML (sympa aussi)... ou SQLIte hein (je radote)

    Citation Envoyé par koala01 Voir le message
    Si, déjà, on peut l'inciter à utiliser std::string std::vector et autres std::, de sera pas si mal...
    Oui car la manipulation de const char* et array C en C++ est de l'autoflagellation !!!

    Citation Envoyé par koala01 Voir le message
    non pas échapper... convertir, en entité html (&lt ou similaire.
    Oui, c'est ce que je voulais dire en fait, mais tu as compris XD

    Pour revenir au topic, en lisant ton code (application.cpp) :

    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
     
     
    bool appel(Requete requete, char *buffer) {
    	// Preparation
    	erreur_index=0;
     
    	// Appel
    	bool reussit = true;
    	strcpy(buffer, "HTTP/1.0 200 OK\r\n");
     
    	//-------------------------------------------
    	// Ressource spéciale gérée par le serveur
    	if (strcmp (requete.path_parts[0], "res") == 0){
    		strcat(buffer, "Content-Type: text/xml\r\n");
    		strcat(buffer, "\r\n");
    		strcat(buffer,	"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n");
    		strcat(buffer, "<reponse service=\"IC4server/ressources\" version=\"1.0\" res=\"");
    		strcat(buffer, requete.path_parts[1]);
    		strcat(buffer, "\">\r\n");
    Non, non, on est pas en C là !!! C'ailleurs en C on n'utilise jamais strcat (qui peut faire déborder les buffers) mais strncat et c'est pénible.

    Ta méthode doit retourner un std::string, et pour accumuler des données dedans tu utilises :

    http://www.cplusplus.com/reference/i...ostringstream/

    qui est comme un buffer mais auto-extensible donc tu n'as pas besoin de te casser la tête.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    		if (strcmp(requete.methode, "GET") == 0){
    Pareil, utilise des strings, après c'est requete.methode == "GET"

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    record.date_adhesion.jour = atoi(getArgVal(NULL, requete, "adh_jour"));
    atoi() c'est le mal !!!
    Il faut valider les entrées. Par exemple si au lieu d'une date tu as "bonjour", il faut afficher "date invalide" et pas "0000-00-00". atoi() ne gère pas du tout les erreurs...

    Je te conseille de donner de la compagnie à getArgVal, par exemple getArgInt() qui prend un paramètre, vérifie que c'est un int, et retourne la valeur, sinon il balance une exception.

    Note que ton truc c'est un peu du SOAP donc tu pourrais faire générer tout ça automatiquement par un module SOAP...

  3. #23
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut


    Je suis impressionné du débat que ma question "basique" implique

    Je suis désolé de n'avoir pas donné suite plus tôt mais j'ai pu "discuter" avec mon prof qui m'interdit l'usage des classes (trop compliqué selon lui alors que pour moi la POO simplifie les problèmes).
    Bref, il ne sera pas possible de terminer un travail générique sur mon code avant la fin du projet. Vu les soucis récurrents rencontrés je ne pense pas me remettre au C prochainement malheureusement.
    J'ai gardé le code général que j'avais écris et je préfère m'en tenir à copier-coller mes fonctions (histoire d'être dans les délais, ça ne me plaît personnellement pas).

    Par contre une chose dont vous parlez retient mon attention : la sérialisation des objets/structures.
    Les fonctions du CRUD que j'ai écris continuent de s'appuyer en permanence sur les fichiers de sauvegarde, on utilise pas de tableaux limitant le nombre d'enregistrement possibles.
    Je fourni un enregistrement structuré de type Membre à ma fonction et j'utilise fwrite. Problème, le fichier est corrompu et je ne sais absolument pas pourquoi.
    Le code est le suivant et reperrez-vous une utilisation "illégale" de fwrite?

    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
     
    // Fonction : postInfos
    // Sauvegarde le contenu d'une variable de type Membre dans le fichier de sauvegarde
    int membres_postInfos (Membre record, char *buffer){
    	char file_url[] = "./data/membres.dat";
    	bool nouveau = false; // Indication de nouveauté de l'enregistrement
    	bool write_result = false; // Indication de succès de l'écriture
    	bool identifying_result = false; // Indication de succès de l'identification
    	int id=record.id; // Identifiant de l'enregistrement
    	int index = id-1; // Index de l'enregistrement dans le fichier
     
     
    	FILE* fichier_1 = fopen (file_url, "wb"); // Premiere ouverture pour "creation du fichier"
    	if (fichier_1 != NULL){ // Si le fichier a bien été ouvert... on le ferme (sic!)
    		fclose (fichier_1);
    	}
     
    	if (id > 0){ // Si l'enregistrement est déjà identifié (parce qu'existant déjà dans le fichier par exemple).
    		record.present = true; // Marquage comme "présent"
    		identifying_result = true; // Succès de facto de l'identification
    	}else{
    		index = membres_placeLibre (); //Sinon on recherche une place libre dans le fichier
    		if (index > -1){ //Si une place libre a été trouvée
    			record.present = true; // Marquage comme "présent"		
    			id = index+1; // Creation d'une ID a partir de cette place
    			record.id = id; // Implentation de cette ID
    			nouveau = true; 
    			identifying_result = true; // Succès de l'identification
    		}
    	}
     
     
    	if (identifying_result){ //Si l'enregistrement a été identifié
    		FILE* fichier = fopen (file_url, "wb");
    		if (fichier != NULL){ //Si le fichier a bien été ouvert
    			if (index > 0){ //Si la place trouvée n'est pas au début du fichier : on se déplace jusque là.
    				printf("fseek : %i\n", fseek(fichier, index * sizeof(record), SEEK_SET));
    			}
     
    			if (fwrite(&record, sizeof(record), 1, fichier)){ // Ecriture de l'enregistrement dans le fichier
    				write_result = true; //Succès de facto de l'ecriture
    			}
     
    			fclose (fichier);
    		}else{
    			printf("[res_postInfos()] - FILE_ERROR : Erreur d'ouverture de %s\n", file_url);
    		}
     
    		if (write_result){ //Si l'écriture a réussie : XML de succès avec la nouvelle ID de l'objet
    			char id_str[5];
    			sprintf(id_str, "%d", id);
    			strcat(buffer, "<post><success>1</success>");
    			if (nouveau){
    				strcat(buffer, "<new_id>");
    				strcat(buffer, id_str);
    				strcat(buffer, "</new_id>");
    			}
    			strcat(buffer, "</post>");
    		}else{
    			// Sinon : ERREUR XML d'écriture
    			Erreur error;
    			strcpy(error.code, "WRITE_ERROR");
    			strcpy(error.titre, "L'ecriture dans le fichier a echoue pour une raison inconnue.");
    			pushErreur(error);
    		}
    	}else{
    		// Sinon : ERREUR XML d'identification
    		Erreur error;
    		strcpy(error.code, "ID_ERROR");
    		strcpy(error.titre, "Impossible d'identifier l'objet avec un nouvel identifiant.");
    		pushErreur(error);
    	}
     
    	return id;
    }
    Biensur, le fichier est directement corrompu à sa création donc à la première insertion d'un enregistrement. placeLibre() renvoi donc 0 ou -1 mais qu'importe puisque je place mon enregistrement en début du fichier (fseek n'est pas exécuté).

    Je vous remercie de l'aide que vous m'avez apporté, j'essayerai de m'initier au C d'une meilleure manière que celle qui a eu cours jusqu'à maintenant.

  4. #24
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 70
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Je suis désolé de n'avoir pas donné suite plus tôt mais j'ai pu "discuter" avec mon prof qui m'interdit l'usage des classes (trop compliqué selon lui alors que pour moi la POO simplifie les problèmes).
    C'est un cours de quel langage en fait ?

  5. #25
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par peufeu Voir le message
    C'est un cours de quel langage en fait ?
    J'ai eu des cours d'algo très rapides en début d'année puis de C/C++ et là enfin c'est l'heure des projets... qui partent à volo.

  6. #26
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 70
    Par défaut
    Merde, un cours de C++ et le prof veut pas que t'utilises de classes, mais merde quoi

  7. #27
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Je suis entièrement de l'avis de peufeu, même s'il l'a énoncé d'une manière assez... verte

    Ceci dit, il faut savoir que,

    1- lorsque tu ouvre (en C) un fichier en passant l'argument "b", tu ouvre le fichier en format... binaire (le très mal nommé), ce qui est clairement incompatible avec le format XML que tu veux utiliser.

    De plus, ce format de fichier (le format "binaire") présente une faiblesse majeure.

    En effet, les différentes architectures qui peuvent utiliser différents "boutismes" (différentes manière de représenter les nombres, principalement, et la signification données aux différents bytes qui les composent), certaines considérant que le premier byte rencontré représente le byte "de poids le plus fort" et d'autres considérant qu'il représente le byte "de poids le plus faible" (pour les deux principales).

    Un fichier binaire écrit sur une machine "petit boutiste" ne pourra donc pas être lu (sans conversion) sur une machine "gros boutiste", et inversement.

    Bref, tu perd énormément de portabilité en écrivant tes fichiers au format binaire

    2- Il n'y a aucun sens à envisager la création d'un fichier qui contient... des valeurs inexistantes.

    Si tu dispose (on va utiliser des nombres arbitraires, pour te permettre de comprendre ) de 35 membres effectifs sur une capacité maximale de 100, il ne sert strictement à rien (que mettrait on dans le fichier pour ceux qui n'existent pas ) d'écrire 100 membres.

    Il faut se limiter à écrire les... 35 membres qui existent réellement.

    Cela te permet, en outre, d'éviter d'avoir un membre qui se contente... de préciser si le membre existe (ou non) dans le fichier... ou de devoir déterminer où placer le nouveau membre

    2- Il faut partir du principe qu'un fichier est "gravé dans le marbre", et qu'il faut donc, à chaque fois que tu veux le modifier autrement qu'en ajoutant des données à la fin, "briser le moule" et tout recommencer.

    Comme tu semble vouloir sérialiser tes données en XML (est-ce déjà seulement intéressant , le format csv ne serait-il pas à l'extrême limite déjà suffisant :quesion, et que ce type de fichier implique que l'ajout de noeuds ne se fait pas "à la fin" (car il faut fermer le noeud racine après le dernier élément existant), la solution passe, bel et bien, par... la réécriture complète de l'ensemble des éléments chaque fois que l'on en ajoute un.

    3- Même si ton prof t'interdit de réfléchir sous la forme de classes pour tes données (ce qui est, en soit, une aberration), il est largement préférable d'utiliser les possibilités du C++ chaque fois que possible, en préférant, par exemple, les flux de fichiers ifstream et ofstream au pointeur sur FILE (issu du C), la classe std::string pour les chaines de caractères, la classe std::vector pour la gestion de tableaux de taille dynamique, ou encore les flux d'entrée / sortie std::cout et std::cin pour l'affichage et l'acquisition de les introductions de l'utilisateur... Autrement, autant coder, réellement, en C

    Toutes ces possibilités offertes par C++ te faciliteront énormément la vie par rapport à ce qui existe d'équivalent en C, et présenteront une sécurité d'utilisation bien plus importante
    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

  8. #28
    Membre Expert
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Par défaut
    Citation Envoyé par peufeu Voir le message
    Merde, un cours de C++ et le prof veut pas que t'utilises de classes, mais merde quoi
    Y'a pire comme approche. Et c'est pas le seul à faire comme ça. (cf "Je me lance" il me semble).

  9. #29
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 70
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Je suis entièrement de l'avis de peufeu, même s'il l'a énoncé d'une manière assez... verte
    Boah, enfin, ce que lui demande son prof, c'est un peu comme repeindre ton plafond avec les orteils XD

    C'est un cours de méditation zen en fait. Less is more

    Ça me rappelle des ex-collègues (ex car virés depuis) : "Nous on programme en C++, on est pas des lopettes, on utilise jamais le debugger, tout au printf !!"

    Mais bon rien qu'à voir le sujet je sentais l'embrouille : un bon prof de C++ te donnerait à faire quelque chose pour lequel le C++ est le bon choix de langage, pas un truc que n'importe qui de sain d'esprit ferait en python, php, ruby, ASP.NET, ou perl, dans lesquels ton programme complet ferait probablement moins de 100 lignes...

    Sinon il est tout à fait possible de gérer le "boutisme" (lol) des fichiers facilement en C++, avec un petit héritage et une classe de fichier avec une fonction template<class T> write( const T& machin ) qui appelle htonl() et ntohl() de manière transparente... après tu fais fichier.write<int>( toto ) et hop. Mais bon les fichiers texte c'est bien aussi : facile à tester, à débugger, ça crash pas...

Discussions similaires

  1. Fonction du type y=
    Par vladskeeper dans le forum Bases de données
    Réponses: 2
    Dernier message: 29/12/2005, 19h54
  2. Réponses: 9
    Dernier message: 22/07/2005, 17h10
  3. Fonction de type between
    Par midnight77 dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 01/04/2005, 15h51
  4. [LG]Resultat de fonction de type Record ?
    Par Mike888 dans le forum Langage
    Réponses: 10
    Dernier message: 24/01/2004, 23h48
  5. Fonction de type tableau
    Par Charles f dans le forum Langage
    Réponses: 5
    Dernier message: 04/08/2002, 15h04

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