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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
#include "c23-fallback.h"
#include <stdio.h>
#define static_assert_compatible(A, B, REASON) \
static_assert(_Generic((typeof(A)*)nullptr, \
typeof(B)*: true, \
default: false), \
"expected compatible types: " REASON ", have " #A " and " #B "")
#ifndef __GNUC__
#define SWAP(X, Y) \
do { \
/* These two variables have the role of function \
parameters. They ensure to evaluate the \
expression X and Y once, since these could be \
complicated lvalue expressions with evaluated \
subexpressions that have side effects. */ \
auto const swap_p1 = &(X); \
auto const swap_p2 = &(Y); \
static_assert_compatible(*swap_p1, *swap_p2, \
"to exchange values, '" \
#X "' and '" #Y \
"' must have compatible types"); \
auto swap_tmp = *swap_p1; \
*swap_p1 = *swap_p2; \
*swap_p2 = swap_tmp; \
} while (false)
#else
#define SWAP(X, Y) \
/* This starts the compound expression construct. */ \
({ \
/* These two variables have the role of function \
parameters. They ensure to evaluate the \
expression X and Y once, since these could be \
complicated lvalue expressions with evaluated \
subexpressions that have side effects. */ \
auto const swap_p1 = &(X); \
auto const swap_p2 = &(Y); \
static_assert_compatible(*swap_p1, *swap_p2, \
"to exchange values, '" \
#X "' and '" #Y \
"' must have compatible types"); \
auto swap_tmp = *swap_p1; \
*swap_p1 = *swap_p2; \
*swap_p2 = swap_tmp; \
/* ensure that the type of the expression is void */ \
(void)0; \
})
#endif
#define isnegative(X) ((X) < 0)
#define MAX_EQSIGN(X, Y) \
/* This starts the compound expression construct. */ \
({ \
/* These two captures play the role of function \
parameters and read X and Y ... */ \
auto const max_x = (X); \
auto const max_y = (Y); \
/* now the body starts */ \
/* types need to have the same signedness */ \
(max_x < max_y) ? max_y : max_x; \
})
#define MAX(X, Y) \
/* This starts the compound expression construct. */ \
({ \
auto const max_x = (X); \
auto const max_y = (Y); \
/* now the body starts */ \
((isnegative(max_x) && !isnegative(max_y)) \
? max_y \
: ((isnegative(max_y) && !isnegative(max_x)) \
? max_x \
: /* both have the same sign */ \
((max_x < max_y) ? max_y : max_x))); \
})
#define mincharacteristic(X, Y) ((issigned(X)<<2)|(issigned(Y)<<1)|1)
#define minunsigned(X, Y) (_Generic(tozero(X)+tozero(Y), typeof(X): tozero(Y), default: tozero(X)))
#define minreturn(X, Y) \
_Generic( \
(char(*)[mincharacteristic(X, Y)]){ 0 }, \
/* both signed, arithmetic conversion */ \
char(*)[4|2|1]: tozero(X)+tozero(Y), \
/* only one signed, use it */ \
char(*)[0|2|1]: tozero(Y), \
char(*)[4|0|1]: tozero(X), \
/* both unsigned, use narrower */ \
char(*)[0|0|1]: minunsigned(X, Y) \
)
#define MIN(X, Y) \
/* This starts the compound expression construct. */ \
({ \
/* These two captures play the role of function \
parameters and read X and Y ... */ \
auto const min_x = (X); \
auto const min_y = (Y); \
/* now the body starts */ \
typedef typeof(minreturn(min_x, min_y)) min_type; \
((isnegative(min_x) && !issigned(min_y)) \
? (min_type)min_x \
: ((isnegative(min_y) && !issigned(min_x)) \
? (min_type)min_y \
: ( \
/* both have the same signedness or are both positive */ \
(min_x < min_y) \
? (min_type)min_x \
: (min_type)min_y))); \
})
int main (int argc, char* argv[static argc + 1]) {
size_t i = argc;
size_t j = argc+1;
printf("%s, %s\n", argv[1], argv[2]);
// Works even with side effects, but don't do that
SWAP(argv[1+(i++)%2], argv[1+(j++)%2]); // don't do that
printf("%s, %s\n", argv[1], argv[2]);
// Works even with side effects, but don't do that
SWAP(argv[1+(i++)%2], argv[1+(j++)%2]); // don't do that
printf("%s, %s\n", argv[1], argv[2]);
printf("%d\t%d\t→\t%d\n", -1, -2, MIN(-1, -2));
printf("%d\t%ldl\t→\t%ldl\n", -2, -1L, MIN(-2, -1L));
printf("%d\t%d\t→\t%d\n", -1, 2, MIN(-1, 2));
printf("%d\t%ldl\t→\t%ldl\n", -1, 2L, MIN(-1, 2L));
printf("%d\t%uu\t→\t%d\n", -1, 2u, MIN(-1, 2u));
printf("%d\t%luul\t→\t%d\n", -1, 2UL, MIN(-1, 2UL));
printf("%uu\t%uu\t→\t%uu\n", -1u, -2u, MIN(-1u, -2u));
printf("%uu\t%luul\t→\t%uu\n", -1u, -2UL, MIN(-1u, -2UL));
static_assert_compatible(1, 0, "both int");
//static_assert_compatible(1, 0L, "both int");
//static_assert_compatible(1L, 0, "both int");
static_assert_compatible(1L, 0L, "both int");
enum A : unsigned long { A = -1UL, };
static_assert_compatible(A, 0UL, "both int");
static_assert_compatible(int[1], int[true], "both int");
}
|