Il est possible d'y parvenir grâce à quelques astuces. Étant donné
typedef enum
{
BLUE,
RED
} color_t;
Définissez ensuite une union fictive qui ne sera pas utilisée par l'appelant, mais qui contient des membres portant les mêmes noms que les constantes de l'énumération :
typedef union
{
color_t BLUE;
color_t RED;
} typesafe_color_t;
Cela est possible car les constantes d'énumération et les noms de membres/variables résident dans des espaces de noms différents.
Ensuite, créez des macros de type fonction :
#define c_assign(var, val) (var) = (typesafe_color_t){ .val = val }.val
#define color_assign(var, val) _Generic((var), color_t: c_assign(var, val))
Ces macros sont ensuite appelées de la manière suivante :
color_t color;
color_assign(color, BLUE);
Explication :
- Le C11
_Generic
garantit que la variable d'énumération est du bon type. Cependant, il ne peut pas être utilisé sur la constante d'énumération BLUE
car il est de type int
.
- Par conséquent, la macro d'aide
c_assign
crée une instance temporaire de l'union fictive, où la syntaxe de l'initialisateur désigné est utilisée pour attribuer la valeur BLUE
à un membre du syndicat nommé BLUE
. Si un tel membre n'existe pas, le code ne sera pas compilé.
- Le membre de l'union du type correspondant est alors copié dans la variable enum.
En fait, nous n'avons pas besoin de la macro d'aide, je divise simplement l'expression pour plus de lisibilité. Il est tout aussi possible d'écrire
#define color_assign(var, val) _Generic((var), \
color_t: (var) = (typesafe_color_t){ .val = val }.val )
Exemples :
color_t color;
color_assign(color, BLUE);// ok
color_assign(color, RED); // ok
color_assign(color, 0); // compiler error
int x;
color_assign(x, BLUE); // compiler error
typedef enum { foo } bar;
color_assign(color, foo); // compiler error
color_assign(bar, BLUE); // compiler error
EDIT
Évidemment, ce qui précède n'empêche pas l'appelant de simplement taper color = garbage;
. Si vous souhaitez bloquer entièrement la possibilité d'utiliser une telle affectation de l'enum, vous pouvez le mettre dans un struct et utiliser la procédure standard d'encapsulation privée avec "type opaque" :
color.h
#include <stdlib.h>
typedef enum
{
BLUE,
RED
} color_t;
typedef union
{
color_t BLUE;
color_t RED;
} typesafe_color_t;
typedef struct col_t col_t; // opaque type
col_t* col_alloc (void);
void col_free (col_t* col);
void col_assign (col_t* col, color_t color);
#define color_assign(var, val) \
_Generic( (var), \
col_t*: col_assign((var), (typesafe_color_t){ .val = val }.val) \
)
couleur.c
#include "color.h"
struct col_t
{
color_t color;
};
col_t* col_alloc (void)
{
return malloc(sizeof(col_t)); // (needs proper error handling)
}
void col_free (col_t* col)
{
free(col);
}
void col_assign (col_t* col, color_t color)
{
col->color = color;
}
main.c
col_t* color;
color = col_alloc();
color_assign(color, BLUE);
col_free(color);
0 votes
Jetez un coup d'œil à ma réponse ci-dessous : stackoverflow.com/questions/39725331/
0 votes
@shjeff C'est assez similaire à certaines des versions structurées postées ci-dessous.