Je cherche des exemples de syndicats, non pas pour comprendre comment fonctionne le syndicat, je l'espère, mais pour voir quel genre de piratage les gens font avec le syndicat.
Alors n'hésitez pas à partager votre hack syndical (avec quelques explications bien sûr :))
Un classique consiste à représenter une valeur de type "inconnu", comme au cœur d'une machine virtuelle simpliste:
typedef enum { INTEGER, STRING, REAL, POINTER } Type;
typedef struct
{
Type type;
union {
int integer;
char *string;
float real;
void *pointer;
} x;
} Value;
En utilisant cela, vous pouvez écrire du code qui gère les "valeurs" sans connaître leur type exact, par exemple implémenter une pile et ainsi de suite.
Puisque c'est en (ancien, pré-C11) C, l'union interne doit recevoir un nom de champ dans le struct
externe. En C++, vous pouvez laisser le union
être anonyme. Choisir ce nom peut être difficile. J'ai tendance à aller avec quelque chose de simple lettre, car il n'est presque jamais référencé isolément et donc il est toujours clair de contexte ce qui se passe.
Le code pour définir une valeur sur un entier pourrait ressembler à ceci:
Value value_new_integer(int v)
{
Value v;
v.type = INTEGER;
v.x.integer = v;
return v;
}
Ici, j'utilise le fait que struct
s peut être retourné directement et traité presque comme des valeurs d'un type primitif (vous pouvez affecter struct
s).
En voici un petit que j'utilise tous les jours:
struct tagVARIANT {
union {
struct __tagVARIANT {
VARTYPE vt;
Word wReserved1;
Word wReserved2;
Word wReserved3;
union {
LONG lVal; /* VT_I4 */
BYTE bVal; /* VT_UI1 */
SHORT iVal; /* VT_I2 */
FLOAT fltVal; /* VT_R4 */
DOUBLE dblVal; /* VT_R8 */
VARIANT_BOOL boolVal; /* VT_BOOL */
_VARIANT_BOOL bool; /* (obsolete) */
SCODE scode; /* VT_ERROR */
CY cyVal; /* VT_CY */
DATE date; /* VT_DATE */
BSTR bstrVal; /* VT_BSTR */
IUnknown * punkVal; /* VT_UNKNOWN */
IDispatch * pdispVal; /* VT_DISPATCH */
SAFEARRAY * parray; /* VT_ARRAY */
BYTE * pbVal; /* VT_BYREF|VT_UI1 */
SHORT * piVal; /* VT_BYREF|VT_I2 */
LONG * plVal; /* VT_BYREF|VT_I4 */
FLOAT * pfltVal; /* VT_BYREF|VT_R4 */
DOUBLE * pdblVal; /* VT_BYREF|VT_R8 */
VARIANT_BOOL *pboolVal; /* VT_BYREF|VT_BOOL */
SCODE * pscode; /* VT_BYREF|VT_ERROR */
CY * pcyVal; /* VT_BYREF|VT_CY */
DATE * pdate; /* VT_BYREF|VT_DATE */
BSTR * pbstrVal; /* VT_BYREF|VT_BSTR */
IUnknown ** ppunkVal; /* VT_BYREF|VT_UNKNOWN */
IDispatch ** ppdispVal; /* VT_BYREF|VT_DISPATCH */
SAFEARRAY ** pparray; /* VT_BYREF|VT_ARRAY */
VARIANT * pvarVal; /* VT_BYREF|VT_VARIANT */
PVOID byref; /* Generic ByRef */
CHAR cVal; /* VT_I1 */
USHORT uiVal; /* VT_UI2 */
ULONG ulVal; /* VT_UI4 */
INT intVal; /* VT_INT */
UINT uintVal; /* VT_UINT */
DECIMAL * pdecVal; /* VT_BYREF|VT_DECIMAL */
CHAR * pcVal; /* VT_BYREF|VT_I1 */
USHORT * puiVal; /* VT_BYREF|VT_UI2 */
ULONG * pulVal; /* VT_BYREF|VT_UI4 */
INT * pintVal; /* VT_BYREF|VT_INT */
UINT * puintVal; /* VT_BYREF|VT_UINT */
} __VARIANT_NAME_3;
} __VARIANT_NAME_2;
DECIMAL decVal;
} __VARIANT_NAME_1;
};
Il s'agit de la définition du type de données de variante d'automation OLE. Comme vous pouvez le voir, il existe de nombreux types possibles. Il existe de nombreuses règles concernant les types que vous pouvez utiliser dans différentes situations, en fonction de la capacités de votre code client prévu. Tous les types ne sont pas pris en charge par toutes les langues.
Les types avec VT_BYREF
après leur utilisation par des langages tels que VBScript qui transmettent les paramètres par référence par défaut. Cela signifie que si vous avez du code qui se soucie des détails de la structure des variantes (tels que C++) appelés par du code qui ne le fait pas (comme VB), vous devez soigneusement déréférencer le paramètre variant si nécessaire.
Les types byref sont également utilisés pour renvoyer les valeurs des fonctions. Il existe également un support pour les types de tableaux utilisant le type SAFEARRAY
étrangement mal nommé - si difficile à utiliser à partir de C++.
Si vous avez un tableau de chaînes, vous pouvez le passer à vbscript, mais il ne peut pas être utilisé (sauf pour imprimer la taille). Pour lire réellement les valeurs, les données du tableau doivent être de type VT_BYREF | VT_BSTR
.
Les unions sont également couramment utilisées au stade de l'analyse lexicale et de l'analyse syntaxique des processeurs de langage, comme les compilateurs et les interprètes. En voici une que je modifie en ce moment.
union {
char c;
int i;
string *s;
double d;
Expression *e;
ExpressionList *el;
fpos_t fp;
}
L'union est utilisée pour associer des valeurs sémantiques aux jetons de l'analyseur lexical et aux productions de l'analyseur. Cette pratique est assez courante dans les générateurs de grammaire, comme yacc, qui fournit un support explicite pour cela. Le syndicat peut détenir n'importe laquelle de ses valeurs, mais une seule à la fois. Par exemple, à n'importe quel point du fichier d'entrée, vous avez soit lu une constante de caractère (stockée dans c
), soit un entier (stocké dans i
) ou un nombre à virgule flottante (stocké dans d
). Le générateur de grammaire fournit une aide considérable pour déterminer laquelle des valeurs est stockée à un moment donné en fonction de la règle en cours de traitement.
Veuillez éviter les "hacks" avec l'union, ils causent des maux de tête de portabilité (endianness, problèmes d'alignement).
Une utilisation légitime de l'union consiste à stocker différents types de données au même endroit, de préférence avec une balise afin que vous sachiez de quel type il s'agit. Voir l'exemple par 1800 INFORMATION.
N'utilisez pas union pour convertir entre les types de données, par exemple d'un entier à plusieurs octets. Utilisez plutôt le décalage et le masquage pour la portabilité.
Nous utilisons des unions pour les messages emballés au travail (C/C++), afin de pouvoir contourner une structure avec une union en tant que membre de données, puis accéder au chemin correct en fonction du champ id dans la structure.
Cela a bien fonctionné jusqu'à ce que quelqu'un écrive la structure dans un fichier, maintenant nous sommes limités aux plus grandes données utilisées dans le fichier, car même s'il existe une version de fichier, personne ne l'a jamais modifiée ...
Ainsi, bien qu'utiles pour le travail en mémoire, évitez de les écrire aveuglément sur le disque ou le réseau.
struct InputEvent
{
enum EventType
{
EventKeyPressed,
EventKeyPressRepeated,
EventKeyReleased,
EventMousePressed,
EventMouseMoved,
EventMouseReleased
} Type;
union
{
unsigned int KeyCode;
struct
{
int x;
int y;
unsigned int ButtonCode;
};
};
};
...
std::vector<InputEvent> InputQueue;
avec le hack d'union, je peux simplement faire un vecteur d'objets. Je suis sûr que cela pourrait être rendu plus propre ... mais cela fonctionne pour moi - KISS
Par coïncidence, je viens d'en utiliser un dans une réponse Stackoverflow ici afin que je puisse traiter un mot qui était composé de champs de 6 bits comme deux entiers non signés de 16 bits.
Il y a des années, j'en utilisais aussi un pour le premier compilateur ARM C - les instructions à l'époque étaient toutes en 32 bits, mais avaient des dispositions différentes selon les instructions exactes. J'avais donc une union pour représenter une instruction ARM, contenant un ensemble de structures qui avaient chacune les champs de bits appropriés pour un type d'instruction spécifique.
#define DWORD unsigned int
#define Word unsigned short
#define BYTE unsigned char
typedef union _DWORD_PART_ {
DWORD dwWord;
struct {
Word dwMSB;
Word dwLSB;
}hw;
struct {
BYTE byMSB;
BYTE byMSBL;
BYTE byLSBH;
BYTE byLSB;
} b;
} DWORD_PART;
C'est un moyen facile d'accéder aux parties des mots. (Une fois que vous avez terminé, tout changement d'endianité de la plate-forme peut également être géré facilement)