data peut contenir différents types de données (structures ou tableau en fonction du type). Faut il que je crée autant de structure "packet" que j'ai de type différent ?
Je ne suis pas sûr de comprendre la question, mais je vais essayer de répondre quand même. :-)
La structure « packet », tu en as défini une, et non seulement tu ne pourras pas en définir d'autres mais en plus tu n'as à priori pas besoin de le faire. Ce sont les types de données pointés par le « void * » qui seront éventuellement multiples et variés.
Et bien sûr, selon le type de données, la fonction de sérialisation et celle de désérialisation ne feront pas la même chose.
Maintenant, si ma réponse ne t'éclaire pas, essaye de poser plus clairement la question.
Cordialement, -- Olivier Miakinen
Bonjour,
Le 02/11/2013 20:58, Matt... a écrit :
Je voudrais transmettre ce genre de structure via une socket :
data peut contenir différents types de données (structures ou tableau en
fonction du type).
Faut il que je crée autant de structure "packet" que j'ai de type
différent ?
Je ne suis pas sûr de comprendre la question, mais je vais essayer de
répondre quand même. :-)
La structure « packet », tu en as défini une, et non seulement tu ne
pourras pas en définir d'autres mais en plus tu n'as à priori pas
besoin de le faire. Ce sont les types de données pointés par le
« void * » qui seront éventuellement multiples et variés.
Et bien sûr, selon le type de données, la fonction de sérialisation
et celle de désérialisation ne feront pas la même chose.
Maintenant, si ma réponse ne t'éclaire pas, essaye de poser plus
clairement la question.
data peut contenir différents types de données (structures ou tableau en fonction du type). Faut il que je crée autant de structure "packet" que j'ai de type différent ?
Je ne suis pas sûr de comprendre la question, mais je vais essayer de répondre quand même. :-)
La structure « packet », tu en as défini une, et non seulement tu ne pourras pas en définir d'autres mais en plus tu n'as à priori pas besoin de le faire. Ce sont les types de données pointés par le « void * » qui seront éventuellement multiples et variés.
Et bien sûr, selon le type de données, la fonction de sérialisation et celle de désérialisation ne feront pas la même chose.
Maintenant, si ma réponse ne t'éclaire pas, essaye de poser plus clairement la question.
La structure « packet », tu en as défini une, et non seulement tu ne pourras pas en définir d'autres mais en plus tu n'as à priori pas besoin de le faire. Ce sont les types de données pointés par le « void * » qui seront éventuellement multiples et variés.
C'est bien mon but, pouvoir mettre dans mon champ data, tout et n'importe quoi. Le soucis, quand j'envoie via la socket ma structure "packet", je n'envoie que le pointeur "data" et non les données (ce qui me semble logique).
Oui.
Et bien sûr, selon le type de données, la fonction de sérialisation et celle de désérialisation ne feront pas la même chose.
Je pense que c'est à ce niveau que je bloque. Pourrais tu m'en dire plus la dessus ?
En très gros, tu dois remplacer un code du genre : write(fd, &thepacket.type, sizeof(thepacket.type)); write(fd, &thepacket.data, sizeof(thepacket.data));
par un code du genre : write(fd, &thepacket.type, sizeof(thepacket.type)); switch(thepacket.type) { case STRUCTOFBIDULE: ... code pour écrire du struct of bidule ... break; case ARRAYOFMACHIN: ... code pour écrire du array of machin ... break; ... }
Mais il y a sans doute mieux, surtout si la machine émettrice n'a pas forcément la même architecture que la machine réceptrice : https://www.google.fr/search?q=s%C3%A9rialisation+en+C
Le 03/11/2013 07:31, Matt... a écrit :
La structure « packet », tu en as défini une, et non seulement tu ne
pourras pas en définir d'autres mais en plus tu n'as à priori pas
besoin de le faire. Ce sont les types de données pointés par le
« void * » qui seront éventuellement multiples et variés.
C'est bien mon but, pouvoir mettre dans mon champ data, tout et n'importe
quoi.
Le soucis, quand j'envoie via la socket ma structure "packet", je n'envoie
que le pointeur "data" et non les données (ce qui me semble logique).
Oui.
Et bien sûr, selon le type de données, la fonction de sérialisation
et celle de désérialisation ne feront pas la même chose.
Je pense que c'est à ce niveau que je bloque. Pourrais tu m'en dire plus
la dessus ?
En très gros, tu dois remplacer un code du genre :
write(fd, &thepacket.type, sizeof(thepacket.type));
write(fd, &thepacket.data, sizeof(thepacket.data));
par un code du genre :
write(fd, &thepacket.type, sizeof(thepacket.type));
switch(thepacket.type) {
case STRUCTOFBIDULE:
... code pour écrire du struct of bidule ...
break;
case ARRAYOFMACHIN:
... code pour écrire du array of machin ...
break;
...
}
Mais il y a sans doute mieux, surtout si la machine émettrice n'a pas
forcément la même architecture que la machine réceptrice :
https://www.google.fr/search?q=s%C3%A9rialisation+en+C
La structure « packet », tu en as défini une, et non seulement tu ne pourras pas en définir d'autres mais en plus tu n'as à priori pas besoin de le faire. Ce sont les types de données pointés par le « void * » qui seront éventuellement multiples et variés.
C'est bien mon but, pouvoir mettre dans mon champ data, tout et n'importe quoi. Le soucis, quand j'envoie via la socket ma structure "packet", je n'envoie que le pointeur "data" et non les données (ce qui me semble logique).
Oui.
Et bien sûr, selon le type de données, la fonction de sérialisation et celle de désérialisation ne feront pas la même chose.
Je pense que c'est à ce niveau que je bloque. Pourrais tu m'en dire plus la dessus ?
En très gros, tu dois remplacer un code du genre : write(fd, &thepacket.type, sizeof(thepacket.type)); write(fd, &thepacket.data, sizeof(thepacket.data));
par un code du genre : write(fd, &thepacket.type, sizeof(thepacket.type)); switch(thepacket.type) { case STRUCTOFBIDULE: ... code pour écrire du struct of bidule ... break; case ARRAYOFMACHIN: ... code pour écrire du array of machin ... break; ... }
Mais il y a sans doute mieux, surtout si la machine émettrice n'a pas forcément la même architecture que la machine réceptrice : https://www.google.fr/search?q=s%C3%A9rialisation+en+C
data peut contenir différents types de données (structures ou tableau en fonction du type).
Euh, non. Pas du tout. « data » contient un pointeur. Les données sont ailleurs, et le pointeur n'est qu'une adresse --au sens postal-- qui permet de savoir où elles sont réellement, entre autres possibilités des pointeurs.
Faut il que je crée autant de structure "packet" que j'ai de type différent ?
Non. Par contre, il faut créer autant de fonctions d'interface (ou d'exportation) qu'il y a de types différents de données.
Un truc que permet le C pour gérer ce genre de cas, est de définir une famille de fonctions, genre (TOTALEMENT NON TESTÉ)
int exporte_MACHIN(socket_t s, void *p) { MACHIN * m = p; int résultat = EINVAL; // ... // sérialise *m et écrit le résultat dans s // ... return résultat; }
int exporte_STRUCTOFBIDULE(socket_t s, void *p) { struct STRUCTOFBIDULE * b = p; int résultat = EINVAL; // ... // sérialise *b et écrit le résultat dans s // ... // Évidemment, s'il y a un MACHIN dans *b, // on peut faire un appel à exporte_MACHIN(s, b->m) // ... return résultat; }
#if 0 // C99 ; suppose que l'encodage des types est compact // Voir ci-après pour gérer les trous (initialisé à 0)... int (* tableau_exporte[])(socket_t, void *) = { [id_MACHIN] = exporte_MACHIN, [id_STRUCTOFBIDULE] = exporte_STRUCTOFBIDULE, // ... }; #else int exporte_erreur(socket_t s, void *p) { /* Insérer pragma pour paramètre non utilisé */ /* Insérer code de débogage pour faire connaître erreur */ return EINVAL; }
#if 1 if (!p || (unsigned)p->type > MAX_id) #else // Pas complètement portable: considère que l'on // peut comparer un pointeur de fonction avec 0. // Mais plus souple pour les erreurs de codification. if (!p || (unsigned)p->type > MAX_id || tableau_exporte[p->type] == 0) #endif // Code pour émettre un message d'avertissement return EINVAL; return tableau_exporte[p->type](s,p->data); }
Si tu avances un peu dans cette direction, tu vas rencontrer un problème avec les tableaux; en particulier, Olivier proposait de gérer des ARRAYOFMACHIN... sauf qu'il va manquer une information, la taille du tableau; il y a trois solutions à cela: la première est de définir la taille (fixe) des tableaux:
typedef MACHIN ARRAYOF25MACHINS[25];
ou // constantes: #define TAILLE_ARRAYOFMACHINS 25
Une autre est de créer une structure avec la taille suivie du tableau:
typedef struct { size_t taille; MACHIN e[taille]; // ou MACHIN e[1] et allocation dynamique } ARRAYOFMACHIN;
L'autre enfin est la solution utilisée pour les chaînes de caractères, utiliser une valeur spécifique (souvent 0 ou un pointeur nul, mais c'est purement conventionnel) pour marquer la fin du « tableau ». Si tu suis cette voie-là, tu vas avoir toutes les contraintes associées au traitement des chaînes de caractères de C, en particulier devoir éviter les débordements.
data peut contenir différents types de données (structures ou tableau en
fonction du type).
Euh, non. Pas du tout.
« data » contient un pointeur. Les données sont ailleurs, et le pointeur
n'est qu'une adresse --au sens postal-- qui permet de savoir où elles
sont réellement, entre autres possibilités des pointeurs.
Faut il que je crée autant de structure "packet" que j'ai de type
différent ?
Non. Par contre, il faut créer autant de fonctions d'interface (ou
d'exportation) qu'il y a de types différents de données.
Un truc que permet le C pour gérer ce genre de cas, est de définir une
famille de fonctions, genre (TOTALEMENT NON TESTÉ)
int exporte_MACHIN(socket_t s, void *p)
{
MACHIN * m = p;
int résultat = EINVAL;
// ...
// sérialise *m et écrit le résultat dans s
// ...
return résultat;
}
int exporte_STRUCTOFBIDULE(socket_t s, void *p)
{
struct STRUCTOFBIDULE * b = p;
int résultat = EINVAL;
// ...
// sérialise *b et écrit le résultat dans s
// ...
// Évidemment, s'il y a un MACHIN dans *b,
// on peut faire un appel à exporte_MACHIN(s, b->m)
// ...
return résultat;
}
#if 0
// C99 ; suppose que l'encodage des types est compact
// Voir ci-après pour gérer les trous (initialisé à 0)...
int (* tableau_exporte[])(socket_t, void *) = {
[id_MACHIN] = exporte_MACHIN,
[id_STRUCTOFBIDULE] = exporte_STRUCTOFBIDULE,
// ...
};
#else
int exporte_erreur(socket_t s, void *p)
{
/* Insérer pragma pour paramètre non utilisé */
/* Insérer code de débogage pour faire connaître erreur */
return EINVAL;
}
#if 1
if (!p || (unsigned)p->type > MAX_id)
#else
// Pas complètement portable: considère que l'on
// peut comparer un pointeur de fonction avec 0.
// Mais plus souple pour les erreurs de codification.
if (!p || (unsigned)p->type > MAX_id
|| tableau_exporte[p->type] == 0)
#endif
// Code pour émettre un message d'avertissement
return EINVAL;
return tableau_exporte[p->type](s,p->data);
}
Si tu avances un peu dans cette direction, tu vas rencontrer un problème
avec les tableaux; en particulier, Olivier proposait de gérer des
ARRAYOFMACHIN... sauf qu'il va manquer une information, la taille du
tableau; il y a trois solutions à cela: la première est de définir la
taille (fixe) des tableaux:
typedef MACHIN ARRAYOF25MACHINS[25];
ou
// constantes:
#define TAILLE_ARRAYOFMACHINS 25
Une autre est de créer une structure avec la taille suivie du tableau:
typedef struct {
size_t taille;
MACHIN e[taille]; // ou MACHIN e[1] et allocation dynamique
} ARRAYOFMACHIN;
L'autre enfin est la solution utilisée pour les chaînes de caractères,
utiliser une valeur spécifique (souvent 0 ou un pointeur nul, mais c'est
purement conventionnel) pour marquer la fin du « tableau ».
Si tu suis cette voie-là, tu vas avoir toutes les contraintes associées
au traitement des chaînes de caractères de C, en particulier devoir
éviter les débordements.
data peut contenir différents types de données (structures ou tableau en fonction du type).
Euh, non. Pas du tout. « data » contient un pointeur. Les données sont ailleurs, et le pointeur n'est qu'une adresse --au sens postal-- qui permet de savoir où elles sont réellement, entre autres possibilités des pointeurs.
Faut il que je crée autant de structure "packet" que j'ai de type différent ?
Non. Par contre, il faut créer autant de fonctions d'interface (ou d'exportation) qu'il y a de types différents de données.
Un truc que permet le C pour gérer ce genre de cas, est de définir une famille de fonctions, genre (TOTALEMENT NON TESTÉ)
int exporte_MACHIN(socket_t s, void *p) { MACHIN * m = p; int résultat = EINVAL; // ... // sérialise *m et écrit le résultat dans s // ... return résultat; }
int exporte_STRUCTOFBIDULE(socket_t s, void *p) { struct STRUCTOFBIDULE * b = p; int résultat = EINVAL; // ... // sérialise *b et écrit le résultat dans s // ... // Évidemment, s'il y a un MACHIN dans *b, // on peut faire un appel à exporte_MACHIN(s, b->m) // ... return résultat; }
#if 0 // C99 ; suppose que l'encodage des types est compact // Voir ci-après pour gérer les trous (initialisé à 0)... int (* tableau_exporte[])(socket_t, void *) = { [id_MACHIN] = exporte_MACHIN, [id_STRUCTOFBIDULE] = exporte_STRUCTOFBIDULE, // ... }; #else int exporte_erreur(socket_t s, void *p) { /* Insérer pragma pour paramètre non utilisé */ /* Insérer code de débogage pour faire connaître erreur */ return EINVAL; }
#if 1 if (!p || (unsigned)p->type > MAX_id) #else // Pas complètement portable: considère que l'on // peut comparer un pointeur de fonction avec 0. // Mais plus souple pour les erreurs de codification. if (!p || (unsigned)p->type > MAX_id || tableau_exporte[p->type] == 0) #endif // Code pour émettre un message d'avertissement return EINVAL; return tableau_exporte[p->type](s,p->data); }
Si tu avances un peu dans cette direction, tu vas rencontrer un problème avec les tableaux; en particulier, Olivier proposait de gérer des ARRAYOFMACHIN... sauf qu'il va manquer une information, la taille du tableau; il y a trois solutions à cela: la première est de définir la taille (fixe) des tableaux:
typedef MACHIN ARRAYOF25MACHINS[25];
ou // constantes: #define TAILLE_ARRAYOFMACHINS 25
Une autre est de créer une structure avec la taille suivie du tableau:
typedef struct { size_t taille; MACHIN e[taille]; // ou MACHIN e[1] et allocation dynamique } ARRAYOFMACHIN;
L'autre enfin est la solution utilisée pour les chaînes de caractères, utiliser une valeur spécifique (souvent 0 ou un pointeur nul, mais c'est purement conventionnel) pour marquer la fin du « tableau ». Si tu suis cette voie-là, tu vas avoir toutes les contraintes associées au traitement des chaînes de caractères de C, en particulier devoir éviter les débordements.
Antoine
espie
Cote serialisation, les choses sont plus sournoises que prevu... souvent ca va marcher jusqu'a ce que ca ne marche pas. Si on envoie des donnees d'une machine a une autre, souvent le premier test se fait sur des machines identiques... mais des qu'on utilise vraiment du reseau, il va y avoir des differences: - de taille des donnees (int sur 32 ou 64 bits, etc) - de codage des donnees (float IEEE ou autre, encodage des chaines de caractere) - d'alignement et de packing (octets intersticiels "de bourrage" dans certaines structures) - d'endianness (ordre des octets dans un short, un int, etc).
C'est pour ca que les protocoles internet "classiques" sont bases sur du texte (telnet, en gros), avec des contraintes supplementaires (retour a la ligne=retour a la ligne dos, crlf, ascii uniquement...)
envoyer directement des donnees binaires... c'est complique, et casse-gueule si on veut que ca soit interoperable.
(confere ntohs et assimiles)
Cote serialisation, les choses sont plus sournoises que prevu... souvent
ca va marcher jusqu'a ce que ca ne marche pas. Si on envoie des donnees
d'une machine a une autre, souvent le premier test se fait sur des machines
identiques... mais des qu'on utilise vraiment du reseau, il va y avoir
des differences:
- de taille des donnees (int sur 32 ou 64 bits, etc)
- de codage des donnees (float IEEE ou autre, encodage des chaines de
caractere)
- d'alignement et de packing (octets intersticiels "de bourrage" dans certaines
structures)
- d'endianness (ordre des octets dans un short, un int, etc).
C'est pour ca que les protocoles internet "classiques" sont bases sur du
texte (telnet, en gros), avec des contraintes supplementaires (retour
a la ligne=retour a la ligne dos, crlf, ascii uniquement...)
envoyer directement des donnees binaires... c'est complique, et casse-gueule
si on veut que ca soit interoperable.
Cote serialisation, les choses sont plus sournoises que prevu... souvent ca va marcher jusqu'a ce que ca ne marche pas. Si on envoie des donnees d'une machine a une autre, souvent le premier test se fait sur des machines identiques... mais des qu'on utilise vraiment du reseau, il va y avoir des differences: - de taille des donnees (int sur 32 ou 64 bits, etc) - de codage des donnees (float IEEE ou autre, encodage des chaines de caractere) - d'alignement et de packing (octets intersticiels "de bourrage" dans certaines structures) - d'endianness (ordre des octets dans un short, un int, etc).
C'est pour ca que les protocoles internet "classiques" sont bases sur du texte (telnet, en gros), avec des contraintes supplementaires (retour a la ligne=retour a la ligne dos, crlf, ascii uniquement...)
envoyer directement des donnees binaires... c'est complique, et casse-gueule si on veut que ca soit interoperable.
(confere ntohs et assimiles)
Erwan David
(Marc Espie) écrivait :
Cote serialisation, les choses sont plus sournoises que prevu... souvent ca va marcher jusqu'a ce que ca ne marche pas. Si on envoie des donnees d'une machine a une autre, souvent le premier test se fait sur des machines identiques... mais des qu'on utilise vraiment du reseau, il va y avoir des differences: - de taille des donnees (int sur 32 ou 64 bits, etc) - de codage des donnees (float IEEE ou autre, encodage des chaines de caractere) - d'alignement et de packing (octets intersticiels "de bourrage" dans certaines structures) - d'endianness (ordre des octets dans un short, un int, etc).
C'est pour ca que les protocoles internet "classiques" sont bases sur du texte (telnet, en gros), avec des contraintes supplementaires (retour a la ligne=retour a la ligne dos, crlf, ascii uniquement...)
envoyer directement des donnees binaires... c'est complique, et casse-gueule si on veut que ca soit interoperable.
(confere ntohs et assimiles)
Si on veut transmettre du binaire il faut en pratique définir le format uniquement sous forme de tableaux d'octets, le protocole définissant les ordres et les interprétations des octets (et encore...)
-- Les simplifications c'est trop compliqué
espie@lain.home (Marc Espie) écrivait :
Cote serialisation, les choses sont plus sournoises que prevu... souvent
ca va marcher jusqu'a ce que ca ne marche pas. Si on envoie des donnees
d'une machine a une autre, souvent le premier test se fait sur des machines
identiques... mais des qu'on utilise vraiment du reseau, il va y avoir
des differences:
- de taille des donnees (int sur 32 ou 64 bits, etc)
- de codage des donnees (float IEEE ou autre, encodage des chaines de
caractere)
- d'alignement et de packing (octets intersticiels "de bourrage" dans certaines
structures)
- d'endianness (ordre des octets dans un short, un int, etc).
C'est pour ca que les protocoles internet "classiques" sont bases sur du
texte (telnet, en gros), avec des contraintes supplementaires (retour
a la ligne=retour a la ligne dos, crlf, ascii uniquement...)
envoyer directement des donnees binaires... c'est complique, et casse-gueule
si on veut que ca soit interoperable.
(confere ntohs et assimiles)
Si on veut transmettre du binaire il faut en pratique définir le format
uniquement sous forme de tableaux d'octets, le protocole définissant les
ordres et les interprétations des octets (et encore...)
Cote serialisation, les choses sont plus sournoises que prevu... souvent ca va marcher jusqu'a ce que ca ne marche pas. Si on envoie des donnees d'une machine a une autre, souvent le premier test se fait sur des machines identiques... mais des qu'on utilise vraiment du reseau, il va y avoir des differences: - de taille des donnees (int sur 32 ou 64 bits, etc) - de codage des donnees (float IEEE ou autre, encodage des chaines de caractere) - d'alignement et de packing (octets intersticiels "de bourrage" dans certaines structures) - d'endianness (ordre des octets dans un short, un int, etc).
C'est pour ca que les protocoles internet "classiques" sont bases sur du texte (telnet, en gros), avec des contraintes supplementaires (retour a la ligne=retour a la ligne dos, crlf, ascii uniquement...)
envoyer directement des donnees binaires... c'est complique, et casse-gueule si on veut que ca soit interoperable.
(confere ntohs et assimiles)
Si on veut transmettre du binaire il faut en pratique définir le format uniquement sous forme de tableaux d'octets, le protocole définissant les ordres et les interprétations des octets (et encore...)