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 :

En C, est-il possible de connaitre le type d'une variable


Sujet :

C

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2018
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2018
    Messages : 138
    Par défaut En C, est-il possible de connaitre le type d'une variable
    Bonjour à tous,

    je voulais savoir, en C, s'il est possible de connaitre le type d'une variable ?
    C'est à dire que j'ai créé une variable "toto", et par une fonction ou autre, d'avoir un retour qui me dise, cette variable est un "unsigned long" par exemple.
    Pour info, il n'y aura pas de pointeur.

    J'ai vu qu'il existe la fonction "typeof", mais impossible de la mettre en marche ou de comprendre réellement comment elle fonctionne.

    D'avance, merci

  2. #2
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 315
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 315
    Billets dans le blog
    5
    Par défaut
    Bonjour.

    Le C est un langage fortement typé. Par défaut, en dehors d'un paramètre de fonction de type void*, tu es contraint de spécifier le type de variable que tu attends. En regardant la fonction TypeOf() les exemples donnés n'utilisent que les macros, ce qui me parait tout à fait cohérent ici. Je dirai donc en résumé que connaître le type d'un paramètre d'une fonction est contradictoire puis que tu le connais par défaut. Si par contre tu veux faire une fonction qui soit "surchargeable" comme en C++ alors il te faut passer par les macros.

    Exemple de la documentation officielle de typeof ():
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #define max(a,b) \
      ({ typeof (a) _a = (a); \
          typeof (b) _b = (b); \
        _a > _b ? _a : _b; })

  3. #3
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 600
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 600
    Par défaut
    Bonjour,

    Il faudrait peut-être reformuler ton besoin. En C, si on a une variable ou un paramètre on a forcément son type, la variable et son type son indissociables!
    Le seul cas de type erasure, comme l'a écrit @gerald3d, c'est void* où on pointe sur de l'inconnu, la type pointé est alors perdu et n'est pas retrouvable.
    Et typeof(x) est un type, le type de x. Mais un type est l'unité nécessaire à la construction du code, par exemple au moment où le code s'exécute, les types n'existent plus et il ne reste que des zones mémoires que le code généré utilise.

  4. #4
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2018
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2018
    Messages : 138
    Par défaut
    Merci pour la réponse, mais ce n'est pas vraiment ce que j'attendais. Là, d'après l'exemple que tu donnes, tu compares deux types pour savoir qui utilisent le plus d'octet.
    Moi je veux vraiment savoir quel type c'est, unsigned char, signed char, unsigned short, signed short, ...., signed long, float, ....

    Pour expliquer rapidement ce que je veux faire, je veux créer des structures avec plein de variables dedans, de tout type et je veux envoyer ces structures pour UART, sachant que de l'autre côté, il ne sait pas ce qu'il doit recevoir. Donc le but est d'envoyer les infos : premier donnée, c'est un flotant, deuxième donnée, c'est un char, etc...

    J'espère avoir avec été clair ?

    Merci

  5. #5
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    Salut à tous,

    Il n'y a pas, à ma connaissance, de mot-clé ou fonction typeof en C standard, pas même en C++ qui dispose pourtant de typeid. En tout cas pas dans les versions éprouvées jusqu'ici. Les exemples cités ci-dessus sont des extensions de GNU. On peut les utiliser à condition d'être OK pour ne pas produire de C standard.

    Une brève recherche montre que typeof n'a fini par être intégré au standard qu'avec C23, donc l'an dernier seulement.

    Citation Envoyé par Amfrey95 Voir le message
    Merci pour la réponse, mais ce n'est pas vraiment ce que j'attendais. Là, d'après l'exemple que tu donnes, tu compares deux types pour savoir qui utilisent le plus d'octet.
    Moi je veux vraiment savoir quel type c'est, unsigned char, signed char, unsigned short, signed short, ...., signed long, float, ....

    Pour expliquer rapidement ce que je veux faire, je veux créer des structures avec plein de variables dedans, de tout type et je veux envoyer ces structures pour UART, sachant que de l'autre côté, il ne sait pas ce qu'il doit recevoir. Donc le but est d'envoyer les infos : premier donnée, c'est un flotant, deuxième donnée, c'est un char, etc...
    C'est le problème qui touche la plupart des langages populaire purement compilés à typage statique : la sérialisation et l'introspection. C'est là même les deux propriétés les plus attendues des versions des standards des langages orientés objet et ce sont celles qui manquent encore le plus à C++ (alors qu'il est déjà nettement plus simple de les implémenter dans un langage interprété ou semi-interprété).

    En théorie, tu ne devrais pas en avoir besoin du tout dans un contexte C parce que la chaîne de traitement est censée être déterministe d'un bout à l'autre. Comment, dans ce cas, concilier ce fait avec ce que l'on vient d'exposer ? Malheureusement en exhibant le fait que ce que tu veux — légitimement — faire à la base n'est pas possible de façon simple : en effet, tu ne peux, à la base, énumérer facilement les membres d'une structure sans connaître le type de celle-ci et tu ne peux pas passer des structures de types différents à une même fonction sans recourir à void * ou en utilisant la surcharge de fonction, qui n'existe pas non plus en C (sauf si cela a été rétroporté avec des versions récentes de la norme, mais je n'ai pas vérifié).

    En revanche, il existe désormais _Generic, depuis C11, qui permet en gros de faire une sorte de select sur des types connus : https://en.cppreference.com/w/c/language/generic

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 154
    Billets dans le blog
    4
    Par défaut
    Les variables sont typées, donc quand tu écris int var; ben var est un... int
    Maintenant si ton problème est de la sérialisation, alors tu dois créer un protocol que chaque partie saura interpréter. Et là tu fais un peu ce que tu veux.
    Pour un cas basique comme ça, je verrais un simple enum qui définit les types supportés, suivi des données brutes.
    Ou alors utiliser quelque chose comme protocol buffer.
    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.

  7. #7
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2018
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2018
    Messages : 138
    Par défaut
    Obisian merci pour ton retour, ceci dit, je ne comprends pas trop l'exemple avec le _Generic
    Bousk, j'avais pensé à un truc comme ça, mais je voulais juste lire n'importe quelle structure sans avoir à la modifier pour pouvoir envoyer les types de ses variables

  8. #8
    Membre Expert Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    794
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 65
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 794
    Par défaut
    Hello,

    Tu n'as pas vraiment besoin d'une structure pour transmettre tes informations. En prolongeant la proposition de @Bousk, tu pourrais utiliser un protocole tout simple: tu envoies d'abord le type de la variable (dans un char ou un int), suivi de la valeur de la variable. Ton récepteur saura alors comment interpréter la donnée reçue.

  9. #9
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2018
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2018
    Messages : 138
    Par défaut
    Justement, je ne veux pas avoir à envoyer en premier un enum sur le type de variable.
    Je veux prendre n'importe quelle structure existante, et pouvoir identifier le type pour chacune des variables qui la compose.

  10. #10
    Membre Expert Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    794
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 65
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 794
    Par défaut
    Tu veux, tu ne veux pas....

    Comment veux-tu que ton récepteur identifie la structure que tu lui as envoyée, si ce n'est en envoyant d'abord un identifiant du contenu envoyé ?

  11. #11
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 498
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 498
    Billets dans le blog
    1
    Par défaut
    J'ai l'impression que tu souhaites pouvoir sérialiser n'importe quelle structure et créer un flux de bytes que tu pourras à envoyer via UART. Tout ça à l'aide d'une fonction unique (et donc universelle).

    Ce n'est pas possible en C, à ma connaissance.

    Une raison nécessaire et suffisante pour trouver que c'est impossible est que tu ne peux pas écrire une fonction qui accepte tous les types possibles sans perdre la connaissance le type réel. Le mieux que tu peux faire en C, c'est void serialize_and_send(void*). Tu peux passer tout et n'importe quoi à cette fonction mais tu es incapable de retrouver le type réel caché derrière le void*.

    On pourrait être tenté de faire une fonction void serialize_and_send(void*, size_t byte_count). On lui passe un pointeur et le nombre de bytes à envoyer avec un sizeof() :

    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
    #include <stdio.h>
     
    void serialize_and_send(void* data, size_t byte_count) {
    	printf("s&s %p %lu", data, byte_count);
    }
     
    typedef struct {
    	char bar;
    	float baz;
    } Foo;
     
    int main() {
    	Foo foo = {1, 2.3};
    	serialize_and_send(&foo, sizeof(foo));
    }
    Le premier problème est bien sûr que tu peux mettre une taille incohérente. En C++, tu pourrais faire un template de fonctions ; en C, une macro sera ta seule option :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #define SERIALIZE_AND_SEND(x) serialize_and_send(&x, sizeof(x))
     
    int main() {
    	Foo foo = {1, 2.3};
    	SERIALIZE_AND_SEND(foo);
    }
    Le second problème ici est le padding. Ce code affiche qu'il enverrait 8 bytes (s'il était pleinement implémenté), alors qu'il y a que 3 bytes réels de données. Est-ce OK d'envoyer du padding ? Si non, il faut s'assurer que toutes les structures envoyées sont packed.

    Dernier problème, et peut-être le pire : sérialiser certains types de données n'a pas de sens. Exemple : un pointeur. Dans le code suivant, tu envoies l'adresse de la structure sur ta machine pour l'exécution du programme. Qu'est ce que la personne à l'autre bout de l'UART peut faire de cette info ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int main() {
    	Foo foo = {1, 2.3};
    	Foo* pointer = &foo;
    	SERIALIZE_AND_SEND(pointer);
    }
    Ces problèmes sont justes sources d'erreur de programmation et des tests devraient permettre de vérifier que tout est OK. Tu es donc capable d'envoyer n'importe quoi sur ton UART. Maintenant la question à 10$ : comment la personne à l'autre bout du fil sait ce qu'elle reçoit ? C'est impossible malheureusement. Il faut que tu envoies le type avant d'envoyer la donnée.... et on a vu depuis le début qu'on a perdu ce type dès lors qu'on a choisi d'utiliser un void*.

    Il manque un truc en C pour faire ça : la réflexion. On en parle pour C++, je ne sais pas où C en est.

    Quelle solution pour toi ? J'ai un long passé de développeur embarqué (en C++ essentiellement), et je n'ai jamais trouvé de solution magique, malheureusement.

    Tu es toujours obligé de supporter un nombre déterminé de types. Soit tu codes toi-même s'il y en a peu et que c'est simple. Tu peux même te faire un générateur de code : tu décris tes données avec du JSON et tu fais un script qui génère des *.h avec les définitions des structures et des *.c avec le code sérialisation. Soit tu utilises des libs pour t'aider, comme https://github.com/nanopb/nanopb (qui fournit justement un format de description et un générateur de code, et qui est une version légère de ProtocolBuffer proposé par @Bousk).

  12. #12
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 504
    Par défaut
    Citation Envoyé par Amfrey95 Voir le message
    Justement, je ne veux pas avoir à envoyer en premier un enum sur le type de variable.
    Je veux prendre n'importe quelle structure existante, et pouvoir identifier le type pour chacune des variables qui la compose.
    Comme expliqué plus haut, tu ne pourras pas passer « n'importe quelle structure » à une fonction (pour sérialisation ou n'importe quelle autre tâche) si celle-ci ne la connaît pas déjà, parce que pour cela, il faudrait :

    • Pouvoir passer des structures de types différents (chaque définition de structure est en soi un type propre, pas seulement un simple « tableau associatif »), sans recourir à void * ;
    • Pouvoir ensuite énumérer les membres de chaque structure, dont le type est déjà indéfini une fois entré dans la fonction ;
    • Obtenir les noms de de ces membres d'une façon ou d'une autre, donc a priori sous forme de chaîne de caractères, qu'il faudrait donc compiler à l'intérieur de l'exécutable, puis transmettre au programme en gérant l'allocation et la libération de la mémoire nécessaire à cela ;
    • « déréférencer » ces noms pour en faire des références au sein de ta structure. On peut déjà plus ou moins le faire avec offsetof() mais ça ne réglerait pas le problème du type, pour la simple raison que celui-ci est toujours résolu à la compilation, et jamais au runtime : une fois le type identifié, le compilateur génère directement le code approprié et ne garde plus trace ni du symbole qui référence l'entité, ni de son type.


    Tout ceci formant précisément la fameuse « introspection » évoquée plus haut.

    En revanche, on comprend aisément que tout ceci devient trivial dès lors que tu utilises un langage interprété puisque tu es obligé de définir tes objets à l'exécution, jamais à la compilation (qui n'existe pas). Tous ces noms de membres sont donc forcément enregistrés en mémoire pendant la durée de vie du programme. Ainsi, si tu écris ton programme en PHP, en Javascript ou en Python, tu vas bénéficier automatiquement de facilités te permettant d'énumérer les propriétés d'un objet ou les clés d'un tableau associatif et, de là, les invoquer. Et par extension, ces mêmes langages proposent en général une façon de sérialiser tout cela en JSON, qui aujourd'hui est de loin la manière la plus facile d'échanger des informations structurées.

    Si toutefois tu consens à te limiter à un ensemble limité de structures déjà connues et définies par toi, alors tu peux recourir depuis C11 à _Generic qui te permet de te soulager d'une partie du travail, sans t'en exonérer totalement, malheureusement.

    _Generic te permet d'effectuer une sorte de switch() sur des types, à la compilation. Le premier paramètre est une expression quelconque dont le compilateur peut reconnaître le type, et le second est une liste qui, pour chaque type cité, va associer une expression donnée avec éventuellement une expression par défaut si default: est utilisé (comme avec switch). Tout cela, bien sûr, va être résolu à la compilation. Cela te permet par exemple de renvoyer automatiquement la valeur de l'enum que tu cherches à éviter mais en automatisant l'opération ou de référencer directement, pour chaque structure, une fonction dédiée à la sérialisation d'un type particulier.

    Tout ceci pourrait ressembler à des lacunes du langage mais en réalité, tout ceci a bien du sens. C'est notamment ce qui peut expliquer les différences subtiles qu'on observe entre une classe, un dictionnaire et un tableau associatif, par exemple. Mais ceci fera l'objet au besoin d'un prochain message.

    (EDIT : Bktero a posté entretemps)

  13. #13
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 785
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 785
    Par défaut
    Et avec des unions , si tu n'as pas de limitations de taille de trame par exemple.
    Ensuite, dans l'union tu ne pourras pas mettre des types complexes comme des tableaux et chaînes de caractères.

    Code partiel:
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef enum e_data_type {
        DATA_TYPE_NONE = 0,
        DATA_TYPE_INT,
        DATA_TYPE_CHAR,
        DATA_TYPE_UNSIGNED_INT,
        DATA_TYPE_FLOAT,
        DATA_TYPE_STR
    } t_data_type;
     
     
    typedef struct s_data_value {
        union {
            int val_int;
            char val_char;
            unsigned int val_uint;
            float val_float;
        };
     
        char* str;
     
        t_data_type type;
    } t_data_value;
     
     
    void data_create(t_data_value* t) {
        if (t != NULL) {
            t->type = DATA_TYPE_NONE;
            t->str  = NULL;
        }
    }
     
    void data_create_int(t_data_value* t, int val) {
        if (t != NULL) {
            t->type = DATA_TYPE_INT;
            t->str  = NULL;
            t->val_int = val;
        }
    }
     
    void data_create_char(t_data_value* t, char val) {
        if (t != NULL) {
            t->type = DATA_TYPE_CHAR;
            t->str  = NULL;
            t->val_char = val;
        }
    }
     
    void data_create_uint(t_data_value* t, unsigned int val) {
        if (t != NULL) {
            t->type = DATA_TYPE_UNSIGNED_INT;
            t->str  = NULL;
            t->val_uint = val;
        }
    }
     
    void data_create_float(t_data_value* t, float val) {
        if (t != NULL) {
            t->type = DATA_TYPE_FLOAT;
            t->str  = NULL;
            t->val_float = val;
        }
    }
     
     
    int main() {
        t_data_value t;
     
        data_create_float(&t, 5.978);
     
        printf("value: %f", t.val_float);
     
     
        return EXIT_SUCCESS;
    }

  14. #14
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2018
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2018
    Messages : 138
    Par défaut
    Merci à tous de vos retours enrichissants.
    Effectivement, je me suis mal exprimé, ce que je voulais dire, c'est que l'émetteur n'ait pas à avoir d'enum dans sa structure, mais par contre, le récepteur, lui doit recevoir un enum, forcément sinon il ne peut pas deviner ce qu'il reçoit.

  15. #15
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 785
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 785
    Par défaut
    Citation Envoyé par Amfrey95 Voir le message
    c'est que l'émetteur n'ait pas à avoir d'enum dans sa structure
    Dans ce cas là tu as 2 solutions

    Tu convertis tout en chaînes de caractères (par exemple 824 -> "824", 1527.2524f -> "1527.2524") et ensuite tu reconvertis tout en nombre.
    Si tu as des chaînes de caractères et des nombres, il faut juste tester les caractères ('0' >= >= '9' + '.' + '-' + '+') ou alors il faut tester le retour des fonctions de conversion strtoXX dans stdlib.h (<- documentation cplusplus en anglais)

    Tu convertis tout en big number et ensuite tu reconvertis tout en nombre. (tu décomposes chaque chiffre qui ensuite va prendre 1 octet).
    Peut-être utiliser 1 bibliothèque pour t'aider, et pour les flottants, je ne sais pas comment cela fonctionne
    Si tu as des chaînes de caractères et des nombres, il faut juste tester les caractères puisque les 10 premiers caractères de la table ASCII ne sont pas imprimables, mais tu n'auras que 10 chiffres (0 -> 9)

  16. #16
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 498
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 498
    Billets dans le blog
    1
    Par défaut
    Je ne pense pas que ça réponde au problème.

    Prenons cette structure !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    typedef struct {
    	char bar;
    	double baz;
    } Foo;
     
     foo = {1, 2.3};
    Tu obtiens "12.3" ? Tu ne peux rien en faire.

    Imaginons un champ supplémentaire zorg qui soit un tableau de caractères. Quelle "convertion en nombres" fais-tu ?

    Mais tu parlais peut-être de faire du JSON ? Même JSON n'est pas la panacée....
    - Il faut quand même faire une fonction de sérialisation (idem pour la déserialisation) par type que tu souhaites manipulées. C'est déjà pas marrant en C++ mais en C, ça doit être bien pénible. Il existe des lib pour aider, comme https://github.com/json-c/json-c
    - Il faut quand même définir un protocol léger sur l'UART pour créer une notion de "paquets". Un paquet pourra alors contenir une string avec un objet JSON dedans.
    - @Amfrey95 se déclare "Développeur en systèmes embarqués" et parle d'UART. Il est probablement sur une plateforme contrainte. JSON a un sacré overhead. A voir si cet overhead est acceptable.

    Personnellement, si le nombre de structures différentes à envoyer est limité, je ferai quelque chose de très basique : je mettrais un marqueur qui dit quel est le type de la donnée derrière et j'enverrais la représentation binaire brute. Ca fonctionne avec des conditions / limitations / dangers
    - Notamment, il n'y a aucune notion de version de protocole donc il faut s'assurer que l'émetteur et le récepteur utilisent bien la même version du code. Inverser l'ordre de 2 champs dans une structure suffit à tout casser.
    - Les structures ne peuvent pas contenir de pointeurs.

    Basiquement (et c'est sûrement à améliorer), tu te retrouves avec quelque chose comme ça :

    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
    #include <stdio.h>
    #include <string.h>
     
    #pragma pack(1)
     
    typedef struct {
    	short a;
    	float b;
    } Foo;
     
    typedef struct {
    	int a;
    	Foo b;
    } SuperFoo;
     
    #pragma pack()
     
    typedef enum {
    	TypeFoo,
    	TypeSuperFoo
    } Type;
     
    void serialize_and_send(Type type, void* data, size_t length) {
    	printf("S&S %d %p %lu\n", type, data, length);
     
    	// Simulate reception
    	switch (type) {
    		case TypeFoo: {
    			Foo foo;
    			memcpy(&foo, data, length);
    			printf("%d %f\n", foo.a, foo.b);
    			break;
    		}
    		case TypeSuperFoo: {
    			SuperFoo super_foo;
    			memcpy(&super_foo, data, length);
    			printf("%d %d %f\n", super_foo.a, super_foo.b.a, super_foo.b.b);
    			break;
    		}
    		default:
    			puts("Unknown type");
    	}
    }
     
    void serialize_and_send_foo(Foo* foo) {
    	serialize_and_send(TypeFoo, foo, sizeof(Foo));
    }
     
    void serialize_and_send_super_foo(SuperFoo* super_foo) {
    	serialize_and_send(TypeSuperFoo, super_foo, sizeof(SuperFoo));
    }
     
    int main() {
    	Foo foo = {1, 2.3f};
    	SuperFoo super_foo = {42, foo};
    	serialize_and_send_foo(&foo);
    	serialize_and_send_super_foo(&super_foo);
    }
    Ca affiche :

    S&S 0 0x7ffe2e09549a 6
    1 2.300000
    S&S 1 0x7ffe2e095490 10
    42 1 2.300000

  17. #17
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 668
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 668
    Par défaut
    Bonjour,

    Le langage n'étant pas introspectif, sans information de type transmise c'est illusoire. Et même s'il l'était, la transmission des seuls contenus perdrait cette information.

    L'union montre bien la limite de l'exercice :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef union {
       int i;
       char cs[4];
       } dum;
    Si je reçois les 4 octets suivant 0x74, 0x79, 0x70, 0x65, est-ce le tableau {'t', 'y', 'p', 'e'} ou l'entier 32 bits 0x65707974 soit 1701869940 ?

    Même l'introspection s'y casserait les dents puisque le type dépend de l'usage donc d'une sémantique et non du code.

    Salutations

Discussions similaires

  1. Réponses: 5
    Dernier message: 23/07/2017, 11h03
  2. Réponses: 3
    Dernier message: 02/08/2015, 14h00
  3. Réponses: 11
    Dernier message: 08/04/2009, 13h29
  4. [Dates] Connaitre le type d'une variable
    Par Sytchev3 dans le forum Langage
    Réponses: 4
    Dernier message: 03/10/2007, 15h36
  5. Connaitre le type d'une variable
    Par mic79 dans le forum Langage
    Réponses: 2
    Dernier message: 04/01/2006, 15h42

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