+/*
+ * SOCKADDR_IN_IN6(CNST, struct sockaddr *sa, SIN, {
+ * // struct sockaddr_in *const SIN; // implicitly
+ * code for inet;
+ * }, {
+ * // struct sockaddr_in6 *const SIN6; // implicitly
+ * code for inet6;
+ * })
+ *
+ * SOCKADDR_IN_IN6_PAIR(CNST, struct sockaddr *sa, SINA,
+ * struct sockaddr *sb, SINB, {
+ * // struct sockaddr_in *const SINA; // implicitly
+ * // struct sockaddr_in *const SINB; // implicitly
+ * code for inet;
+ * },{
+ * // struct sockaddr_in6 *const SINA6; // implicitly
+ * // struct sockaddr_in6 *const SINB6; // implicitly
+ * code for inet6;
+ * });
+ *
+ * SOCKADDR_IN_IN6_OTHER(CNST, struct sockaddr *sa, SIN, { in }, { in6 }, {
+ * code for other address family
+ * })
+ *
+ * AF_IN_IN6_OTHER(af, { in }, { in6 }, { other })
+ *
+ * Executes the first or second block according to the AF in sa. CNST
+ * may be `const' or empty. For _PAIR, sa and sb must be same AF.
+ *
+ * All except _OTHER handle unknown AFs with unknown_af.
+ *
+ * Code blocks may not contain , outside parens.
+ */
+#define AF_IN_IN6_OTHER(af, for_inet, for_inet6, other) \
+ if ((af) == AF_INET) { \
+ for_inet \
+ } else if ((af) == AF_INET6) { \
+ for_inet6 \
+ } else { \
+ other \
+ }
+#define SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, for_inet6, other) \
+ AF_IN_IN6_OTHER((sa)->sa_family, { \
+ cnst struct sockaddr_in *const sin = (cnst void*)(sa); \
+ for_inet \
+ }, { \
+ cnst struct sockaddr_in6 *const sin##6 = (cnst void*)(sa); \
+ for_inet6 \
+ }, \
+ other \
+ )
+#define SOCKADDR_IN_IN6(cnst, sa, sin, for_inet, for_inet6) \
+ SOCKADDR_IN_IN6_OTHER(cnst, sa, sin, for_inet, for_inet6, { \
+ unknown_af((sa)->sa_family); \
+ })
+#define SOCKADDR_IN_IN6_PAIR(cnst, sa, sina, sb, sinb, for_inet, for_inet6) \
+ do{ \
+ assert((sa)->sa_family == (sb)->sa_family); \
+ SOCKADDR_IN_IN6(cnst, sa, sina, { \
+ cnst struct sockaddr_in *const sinb = (cnst void*)(sb); \
+ for_inet \
+ }, { \
+ cnst struct sockaddr_in6 *const sinb##6 = (cnst void*)(sb); \
+ for_inet6 \
+ }); \
+ }while(0)
+