1 /* 2 * Copyright (c) 2023-2024 Apple Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef MDNS_GENERAL_H 18 #define MDNS_GENERAL_H 19 20 /*! 21 * @brief 22 * Evaluates to non-zero if compiling for a particular platform. 23 * 24 * @param PLATFORM_NAME 25 * The name of the platform, e.g., APPLE. 26 */ 27 #define MDNS_PLATFORM(PLATFORM_NAME) MDNS_PLATFORM_PRIVATE_DEFINITION_ ## PLATFORM_NAME () 28 29 /*! 30 * @brief 31 * Evaluates to non-zero if compiling for Apple OSes. 32 * 33 * @discussion 34 * `__APPLE__` is defined when compiling for Apple, see 35 * <https://developer.apple.com/library/archive/documentation/Porting/Conceptual/PortingUnix/compiling/compiling.html>. 36 */ 37 #if defined(__APPLE__) && __APPLE__ 38 #define MDNS_PLATFORM_PRIVATE_DEFINITION_APPLE() 1 39 #else 40 #define MDNS_PLATFORM_PRIVATE_DEFINITION_APPLE() 0 41 #endif 42 43 #if MDNS_PLATFORM(APPLE) 44 #include <TargetConditionals.h> 45 #endif 46 47 /*! 48 * @brief 49 * Evaluates to non-zero if compiling for a particular OS. 50 * 51 * @param OS_NAME 52 * The name of the OS, e.g., macOS, iOS, etc. 53 */ 54 #define MDNS_OS(OS_NAME) MDNS_OS_PRIVATE_DEFINITION_ ## OS_NAME () 55 56 /*! 57 * @brief 58 * Evaluates to non-zero if compiling for macOS. 59 * 60 * @discussion 61 * Use `MDNS_OS(macOS)` instead of using this macro directly. 62 */ 63 #if defined(TARGET_OS_OSX) && TARGET_OS_OSX 64 #define MDNS_OS_PRIVATE_DEFINITION_macOS() 1 65 #else 66 #define MDNS_OS_PRIVATE_DEFINITION_macOS() 0 67 #endif 68 69 /*! 70 * @brief 71 * Evaluates to non-zero if compiling for iOS. 72 * 73 * @discussion 74 * Use `MDNS_OS(iOS)` instead of using this macro directly. 75 */ 76 #if defined(TARGET_OS_IOS) && TARGET_OS_IOS 77 #define MDNS_OS_PRIVATE_DEFINITION_iOS() 1 78 #else 79 #define MDNS_OS_PRIVATE_DEFINITION_iOS() 0 80 #endif 81 82 /*! 83 * @brief 84 * Evaluates to non-zero if compiling for watchOS. 85 * 86 * @discussion 87 * Use `MDNS_OS(watchOS)` instead of using this macro directly. 88 */ 89 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH 90 #define MDNS_OS_PRIVATE_DEFINITION_watchOS() 1 91 #else 92 #define MDNS_OS_PRIVATE_DEFINITION_watchOS() 0 93 #endif 94 95 /*! 96 * @brief 97 * Evaluates to non-zero if compiling for tvOS. 98 * 99 * @discussion 100 * Use `MDNS_OS(tvOS)` instead of using this macro directly. 101 */ 102 #if defined(TARGET_OS_TV) && TARGET_OS_TV 103 #define MDNS_OS_PRIVATE_DEFINITION_tvOS() 1 104 #else 105 #define MDNS_OS_PRIVATE_DEFINITION_tvOS() 0 106 #endif 107 108 // Time conversion constants 109 110 #define MDNS_NANOSECONDS_PER_SECOND 1000000000 111 #define MDNS_MILLISECONDS_PER_SECOND 1000 112 #define MDNS_MILLISECONDS_PER_MINUTE (MDNS_MILLISECONDS_PER_SECOND * MDNS_SECONDS_PER_MINUTE) 113 #define MDNS_MILLISECONDS_PER_HOUR (MDNS_MILLISECONDS_PER_SECOND * MDNS_SECONDS_PER_HOUR) 114 #define MDNS_SECONDS_PER_MINUTE 60 115 #define MDNS_SECONDS_PER_HOUR (MDNS_SECONDS_PER_MINUTE * MDNS_MINUTES_PER_HOUR) 116 #define MDNS_SECONDS_PER_DAY (MDNS_SECONDS_PER_HOUR * MDNS_HOUR_PER_DAY) 117 #define MDNS_MINUTES_PER_HOUR 60 118 #define MDNS_HOUR_PER_DAY 24 119 120 // Clang's __has_*() builtin macros are defined as zero if not defined. 121 122 #if !defined(__has_attribute) 123 #define __has_attribute(X) 0 124 #endif 125 #if !defined(__has_extension) 126 #define __has_extension(X) 0 127 #endif 128 #if !defined(__has_feature) 129 #define __has_feature(X) 0 130 #endif 131 132 /*! 133 * @brief 134 * Evaluates to non-zero if the compiler is Clang. 135 * 136 * @discussion 137 * __clang__ is defined when compiling with Clang, see 138 * <https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros>. 139 */ 140 #if defined(__clang__) 141 #define MDNS_COMPILER_IS_CLANG() 1 142 #else 143 #define MDNS_COMPILER_IS_CLANG() 0 144 #endif 145 146 /*! 147 * @brief 148 * Evaluates to non-zero if the compiler is Clang and its version is at least a specified version. 149 * 150 * @param MAJOR 151 * The specified version's major number. 152 * 153 * @param MINOR 154 * The specified version's minor number. 155 * 156 * @param PATCH_LEVEL 157 * The specified version's patch level. 158 * 159 * @discussion 160 * Clang version numbers are of the form "<major number>.<minor number>.<patch level>". See 161 * <https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros> 162 */ 163 #if MDNS_COMPILER_IS_CLANG() 164 #define MDNS_CLANG_VERSION_IS_AT_LEAST(MAJOR, MINOR, PATCH_LEVEL) ( \ 165 (__clang_major__ > (MAJOR)) || ( \ 166 (__clang_major__ == (MAJOR)) && ( \ 167 (__clang_minor__ > (MINOR)) || ( \ 168 (__clang_minor__ == (MINOR)) && (__clang_patchlevel__ >= (PATCH_LEVEL)) \ 169 ) \ 170 ) \ 171 ) \ 172 ) 173 #else 174 #define MDNS_CLANG_VERSION_IS_AT_LEAST(MAJOR, MINOR, PATCH_LEVEL) 0 175 #endif 176 177 /*! 178 * @brief 179 * Stringizes the argument and passes it to the _Pragma() operator, which takes a string literal argument. 180 * 181 * @param ARG 182 * The argument. 183 * 184 * @discussion 185 * Useful for escaping double quotes. For example, 186 * 187 * MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic ignored "-Wpadded") 188 * 189 * turns into 190 * 191 * _Pragma("clang diagnostic ignored \"-Wpadded\"") 192 * 193 * See <https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html>. 194 */ 195 #define MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(ARG) _Pragma(#ARG) 196 197 /*! 198 * @brief 199 * For Clang, starts ignoring the specified warning diagnostic flag. 200 * 201 * @param WARNING 202 * The warning diagnostic flag. 203 * 204 * @discussion 205 * Use MDNS_CLANG_IGNORE_WARNING_END() to undo the effect of this macro. 206 */ 207 #if MDNS_COMPILER_IS_CLANG() 208 #define MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING) \ 209 _Pragma("clang diagnostic push") \ 210 MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic ignored #WARNING) 211 #else 212 #define MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING) 213 #endif 214 215 /*! 216 * @brief 217 * Use to undo the effect of a previous MDNS_CLANG_IGNORE_WARNING_BEGIN(). 218 */ 219 #if MDNS_COMPILER_IS_CLANG() 220 #define MDNS_CLANG_IGNORE_WARNING_END() _Pragma("clang diagnostic pop") 221 #else 222 #define MDNS_CLANG_IGNORE_WARNING_END() 223 #endif 224 225 /*! 226 * @brief 227 * An alternative version of MDNS_CLANG_IGNORE_WARNING_BEGIN() that looks nicer when used among statements. 228 * 229 * @discussion 230 * This version looks nicer when used among C statements. Here's an example: 231 * 232 * mdns_clang_ignore_warning_begin(-Wformat-nonliteral); 233 * const int n = vsnprintf(dst, len, fmt, args); 234 * mdns_clang_ignore_warning_end(); 235 */ 236 #define mdns_clang_ignore_warning_begin(WARNING) \ 237 MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING) \ 238 do {} while (0) 239 240 /*! 241 * @brief 242 * An alternative version of MDNS_CLANG_IGNORE_WARNING_END() that looks nicer when used among statements. 243 * 244 * @discussion 245 * This version looks nicer when used among C statements. Here's an example: 246 * 247 * mdns_clang_ignore_warning_begin(-Wformat-nonliteral); 248 * const int n = vsnprintf(dst, len, fmt, args); 249 * mdns_clang_ignore_warning_end(); 250 */ 251 #define mdns_clang_ignore_warning_end() \ 252 MDNS_CLANG_IGNORE_WARNING_END() \ 253 do {} while (0) 254 255 /*! 256 * @brief 257 * For Clang, starts ignoring the -Wunaligned-access warning diagnostic flag. 258 * 259 * @discussion 260 * The -Wunaligned-access is new in clang version 14.0.3. This macro allow us to conditionally ignore 261 * -Wunaligned-access with Clang 14.0.3 or later. This avoids -Wunknown-warning-option warnings with 262 * earlier Clang versions, which don't recognize -Wunaligned-access. 263 */ 264 #if MDNS_CLANG_VERSION_IS_AT_LEAST(14, 0, 3) 265 #define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN() MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wunaligned-access) 266 #else 267 #define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN() 268 #endif 269 270 /*! 271 * @brief 272 * Undoes the effect of a previous MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN(). 273 */ 274 #if MDNS_CLANG_VERSION_IS_AT_LEAST(14, 0, 3) 275 #define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_END() MDNS_CLANG_IGNORE_WARNING_END() 276 #else 277 #define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_END() 278 #endif 279 280 /*! 281 * @brief 282 * For Clang, starts ignoring the -Wincompatible-function-pointer-types-strict warning diagnostic flag. 283 * 284 * @discussion 285 * -Wincompatible-function-pointer-types-strict is like -Wincompatible-function-pointer-types, but is more 286 * strict in that it warns about function pointer types that are not identical but are still compatible. 287 * 288 * The -Wincompatible-function-pointer-types-strict is new in clang version 16.0.0 (see 289 * https://releases.llvm.org/16.0.0/tools/clang/docs/ReleaseNotes.html). This macro allow us to 290 * conditionally ignore -Wincompatible-function-pointer-types-strict with Clang 16.0.0 or later. This 291 * avoids -Wunknown-warning-option warnings with earlier Clang versions, which don't recognize 292 * -Wincompatible-function-pointer-types-strict. 293 */ 294 #if MDNS_CLANG_VERSION_IS_AT_LEAST(16, 0, 0) 295 #define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN() \ 296 MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wincompatible-function-pointer-types-strict) 297 #else 298 #define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN() 299 #endif 300 301 /*! 302 * @brief 303 * Undoes the effect of a previous 304 * MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN(). 305 */ 306 #if MDNS_CLANG_VERSION_IS_AT_LEAST(16, 0, 0) 307 #define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_END() MDNS_CLANG_IGNORE_WARNING_END() 308 #else 309 #define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_END() 310 #endif 311 312 /*! 313 * @brief 314 * For Clang, treats the specified warning diagnostic flag as an error. 315 * 316 * @param WARNING 317 * The warning diagnostic flag. 318 * 319 * @discussion 320 * Use MDNS_CLANG_TREAT_WARNING_AS_ERROR_END() to undo the effect of this macro. 321 */ 322 #if MDNS_COMPILER_IS_CLANG() 323 #define MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN(WARNING) \ 324 _Pragma("clang diagnostic push") \ 325 MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic error #WARNING) 326 #else 327 #define MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN(WARNING) 328 #endif 329 330 /*! 331 * @brief 332 * Undoes the effect of a previous MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN(). 333 */ 334 #if MDNS_COMPILER_IS_CLANG() 335 #define MDNS_CLANG_TREAT_WARNING_AS_ERROR_END() _Pragma("clang diagnostic pop") 336 #else 337 #define MDNS_CLANG_TREAT_WARNING_AS_ERROR_END() 338 #endif 339 340 /*! 341 * @brief 342 * For Clang, specifies that pointers without a nullability qualifier are _Nonnull. 343 * 344 * @discussion 345 * See <https://clang.llvm.org/docs/AttributeReference.html#nullability-attributes>. 346 */ 347 #if (MDNS_COMPILER_IS_CLANG() && __has_feature(assume_nonnull)) 348 #define MDNS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") 349 #else 350 #define MDNS_ASSUME_NONNULL_BEGIN 351 #endif 352 353 /*! 354 * @brief 355 * Undoes the effect of a previous MDNS_ASSUME_NONNULL_BEGIN. 356 */ 357 #if (MDNS_COMPILER_IS_CLANG() && __has_feature(assume_nonnull)) 358 #define MDNS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") 359 #else 360 #define MDNS_ASSUME_NONNULL_END 361 #endif 362 363 /*! 364 * @brief 365 * If supported, an attribute for closed enumeration definitions. 366 * 367 * @discussion 368 * See <https://clang.llvm.org/docs/AttributeReference.html#enum-extensibility>. 369 */ 370 #if __has_attribute(enum_extensibility) 371 #define MDNS_ENUM_ATTR_CLOSED __attribute__((enum_extensibility(closed))) 372 #else 373 #define MDNS_ENUM_ATTR_CLOSED 374 #endif 375 376 /*! 377 * @brief 378 * If supported, defines a fixed-width closed enumeration. 379 * 380 * @param NAME 381 * The name of the enumeration. 382 * 383 * @param UNDERLYING_TYPE 384 * The enumeration's underlying type. 385 * 386 * @param ... 387 * The enumerator list. 388 * 389 * @discussion 390 * See <https://clang.llvm.org/docs/LanguageExtensions.html#enumerations-with-a-fixed-underlying-type> and 391 * <https://clang.llvm.org/docs/AttributeReference.html#enum-extensibility>. 392 */ 393 #if (__has_feature(objc_fixed_enum) || __has_extension(cxx_fixed_enum) || __has_extension(cxx_strong_enums)) 394 #define MDNS_CLOSED_ENUM(NAME, UNDERLYING_TYPE, ...) \ 395 typedef enum : UNDERLYING_TYPE { \ 396 __VA_ARGS__ \ 397 } MDNS_ENUM_ATTR_CLOSED NAME 398 #else 399 #define MDNS_CLOSED_ENUM(NAME, UNDERLYING_TYPE, ...) \ 400 typedef UNDERLYING_TYPE NAME; \ 401 enum NAME ## _enum { \ 402 __VA_ARGS__ \ 403 } MDNS_ENUM_ATTR_CLOSED 404 #endif 405 406 /*! 407 * @brief 408 * If supported, an attribute for flag-like enumeration definitions. 409 * 410 * @discussion 411 * See <https://clang.llvm.org/docs/AttributeReference.html#flag-enum>. 412 */ 413 #if __has_attribute(flag_enum) 414 #define MDNS_ENUM_ATTR_FLAG __attribute__((flag_enum)) 415 #else 416 #define MDNS_ENUM_ATTR_FLAG 417 #endif 418 419 /*! 420 * @brief 421 * If supported, defines a fixed-width closed flag-like enumeration. 422 * 423 * @param NAME 424 * The name of the enumeration. 425 * 426 * @param UNDERLYING_TYPE 427 * The enumeration's underlying type. 428 * 429 * @param ... 430 * The enumeratior list. 431 * 432 * @discussion 433 * See <https://clang.llvm.org/docs/LanguageExtensions.html#enumerations-with-a-fixed-underlying-type> and 434 * <https://clang.llvm.org/docs/AttributeReference.html#flag-enum>. 435 */ 436 #if (__has_feature(objc_fixed_enum) || __has_extension(cxx_fixed_enum) || __has_extension(cxx_strong_enums)) 437 #define MDNS_CLOSED_OPTIONS(NAME, UNDERLYING_TYPE, ...) \ 438 typedef enum : UNDERLYING_TYPE { \ 439 __VA_ARGS__ \ 440 } MDNS_ENUM_ATTR_CLOSED MDNS_ENUM_ATTR_FLAG NAME 441 #else 442 #define MDNS_CLOSED_OPTIONS(NAME, UNDERLYING_TYPE, ...) \ 443 typedef UNDERLYING_TYPE NAME; \ 444 enum NAME ## _enum { \ 445 __VA_ARGS__ \ 446 } MDNS_ENUM_ATTR_CLOSED MDNS_ENUM_ATTR_FLAG 447 #endif 448 449 /*! 450 * @brief 451 * For compatibility with C++, marks the beginning of C function declarations. 452 * 453 * @discussion 454 * See <https://en.cppreference.com/w/cpp/language/language_linkage>. 455 */ 456 #if defined(__cplusplus) 457 #define MDNS_C_DECLARATIONS_BEGIN extern "C" { 458 #else 459 #define MDNS_C_DECLARATIONS_BEGIN 460 #endif 461 462 /*! 463 * @brief 464 * For compatibility with C++, marks the end of C function declarations. 465 * 466 * @discussion 467 * This is the counterpart to MDNS_C_DECLARATIONS_BEGIN. 468 */ 469 #if defined(__cplusplus) 470 #define MDNS_C_DECLARATIONS_END } 471 #else 472 #define MDNS_C_DECLARATIONS_END 473 #endif 474 475 /*! 476 * @brief 477 * Evaluates to non-zero if the compiler conforms to a specific minimum C standard. 478 * 479 * @param STANDARD 480 * The C standard. 481 */ 482 #define MDNS_C_STANDARD_IS_AT_LEAST(STANDARD) MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_ ## STANDARD () 483 484 /*! 485 * @brief 486 * Evaluates to non-zero if the compiler confroms to the C99 standard or later. 487 * 488 * @discussion 489 * __STDC_VERSION__ is a predefined macro that expands to 199901L for the C99 standard. See 490 * <https://en.cppreference.com/w/c/preprocessor/replace>. 491 * 492 * Use `MDNS_C_STANDARD_IS_AT_LEAST(C99)` instead of using this macro directly. 493 */ 494 #if defined(__STDC_VERSION__) 495 #define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C99() (__STDC_VERSION__ >= 199901L) 496 #else 497 #define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C99() 0 498 #endif 499 500 /*! 501 * @brief 502 * Evaluates to non-zero if the compiler confroms to the C11 standard or later. 503 * 504 * @discussion 505 * __STDC_VERSION__ is a predefined macro that expands to 201112L for the C11 standard. See 506 * <https://en.cppreference.com/w/c/preprocessor/replace>. 507 * 508 * Use `MDNS_C_STANDARD_IS_AT_LEAST(C11)` instead of using this macro directly. 509 */ 510 #if defined(__STDC_VERSION__) 511 #define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C11() (__STDC_VERSION__ >= 201112L) 512 #else 513 #define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C11() 0 514 #endif 515 516 /*! 517 * @brief 518 * Evaluates to non-zero if the compiler conforms to a specific minimum C++ standard. 519 * 520 * @param STANDARD 521 * The C standard. 522 */ 523 #define MDNS_CPP_STANDARD_IS_AT_LEAST(STANDARD) MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_ ## STANDARD () 524 525 /*! 526 * @brief 527 * Evaluates to non-zero if the compiler confroms to the C++11 standard or later. 528 * 529 * @discussion 530 * __cplusplus is a predefined macro that expands to 201103L for the C++11 standard. See 531 * <https://en.cppreference.com/w/cpp/preprocessor/replace>. 532 * 533 * Use `MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11)` instead of using this macro directly. 534 */ 535 #if defined(__cplusplus) 536 #define MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_CPP11() (__cplusplus >= 201103L) 537 #else 538 #define MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_CPP11() 0 539 #endif 540 541 /*! 542 * @brief 543 * Causes a compile-time error if an expression evaluates to false. 544 * 545 * @param EXPRESSION 546 * The expression. 547 * 548 * @param MESSAGE 549 * If supported, a sting literal to include as a diagnostic message if the expression evaluates to false. 550 */ 551 #if MDNS_C_STANDARD_IS_AT_LEAST(C11) 552 #define mdns_compile_time_check(EXPRESSION, MESSAGE) _Static_assert(EXPRESSION, MESSAGE) 553 #elif MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11) 554 #define mdns_compile_time_check(EXPRESSION, MESSAGE) static_assert(EXPRESSION, MESSAGE) 555 #elif defined(__cplusplus) 556 #define mdns_compile_time_check(EXPRESSION, MESSAGE) \ 557 extern "C" int mdns_compile_time_check_failed[(EXPRESSION) ? 1 : -1] 558 #else 559 #define mdns_compile_time_check(EXPRESSION, MESSAGE) \ 560 extern int mdns_compile_time_check_failed[(EXPRESSION) ? 1 : -1] 561 #endif 562 563 /*! 564 * @brief 565 * Causes a compile-time error if an expression evaluates to false. 566 * 567 * @param EXPRESSION 568 * The expression. 569 * 570 * @discussion 571 * This macro is meant to be used in a local scope, i.e., inside of a function or a block. For the global 572 * scope, use `mdns_compile_time_check()`. 573 * 574 * The fallback implementation is based on code from 575 * <https://www.drdobbs.com/compile-time-assertions/184401873>. 576 */ 577 #if MDNS_C_STANDARD_IS_AT_LEAST(C11) 578 #define mdns_compile_time_check_local(EXPRESSION) _Static_assert(EXPRESSION, "Compile-time assertion failed.") 579 #elif MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11) 580 #define mdns_compile_time_check_local(EXPRESSION) static_assert(EXPRESSION, "Compile-time assertion failed.") 581 #else 582 #define mdns_compile_time_check_local(EXPRESSION) \ 583 do { \ 584 enum { \ 585 mdns_compile_time_check_local_failed = 1 / ((EXPRESSION) ? 1 : 0) \ 586 }; \ 587 } while (0) 588 #endif 589 590 /*! 591 * @brief 592 * Determines at compile-time if the size of a type exceeds a specified maximum. 593 * 594 * @param TYPE 595 * The type. 596 * 597 * @param MAX_SIZE 598 * The maximum size in bytes. 599 */ 600 #define mdns_compile_time_max_size_check(TYPE, MAX_SIZE) \ 601 mdns_compile_time_check(sizeof(TYPE) <= MAX_SIZE, "The size of " # TYPE " exceeds max size of '" # MAX_SIZE "'.") 602 603 /*! 604 * @brief 605 * Determines the size of an array's element type. 606 * 607 * @param ARRAY 608 * The array. 609 */ 610 #define mdns_sizeof_element(ARRAY) sizeof(ARRAY[0]) 611 612 /*! 613 * @brief 614 * Determines the size of a member variable from a struct or union. 615 * 616 * @param TYPE 617 * The type name of the struct or union. 618 * 619 * @param MEMBER 620 * The name of the member variable. 621 */ 622 #define mdns_sizeof_member(TYPE, MEMBER) sizeof(((TYPE *)0)->MEMBER) 623 624 /*! 625 * @brief 626 * Determines the number of elements in an array. 627 * 628 * @param ARRAY 629 * The array. 630 */ 631 #define mdns_countof(ARRAY) (sizeof(ARRAY) / mdns_sizeof_element(ARRAY)) 632 633 /*! 634 * @brief 635 * If an expression evaluates to false, transfers control to a goto label. 636 * 637 * @param EXPRESSION 638 * The expression. 639 * 640 * @param LABEL 641 * The location's goto label. 642 * 643 * @discussion 644 * No debugging information is logged. 645 */ 646 #define mdns_require_quiet(EXPRESSION, LABEL) \ 647 do { \ 648 if (!(EXPRESSION)) { \ 649 goto LABEL; \ 650 } \ 651 } while (0) 652 653 /*! 654 * @brief 655 * If an expression evaluates to false, executes an action, then transfers control to a goto label. 656 * 657 * @param EXPRESSION 658 * The expression. 659 * 660 * @param LABEL 661 * The goto label. 662 * 663 * @param ACTION 664 * The code to execute. 665 * 666 * @discussion 667 * No debugging information is logged. 668 */ 669 #define mdns_require_action_quiet(EXPRESSION, LABEL, ACTION) \ 670 do { \ 671 if (!(EXPRESSION)) { \ 672 { \ 673 ACTION; \ 674 } \ 675 goto LABEL; \ 676 } \ 677 } while (0) 678 679 /*! 680 * @brief 681 * If an error code is non-zero, transfers control to a goto label. 682 * 683 * @param ERROR 684 * The error code. 685 * 686 * @param LABEL 687 * The location's goto label. 688 * 689 * @discussion 690 * No debugging information is logged. 691 */ 692 #define mdns_require_noerr_quiet(ERROR, LABEL) mdns_require_quiet(!(ERROR), LABEL) 693 694 /*! 695 * @brief 696 * If an error code is non-zero, executes an action, then transfers control to a goto label. 697 * 698 * @param ERROR 699 * The error code. 700 * 701 * @param LABEL 702 * The location's goto label. 703 * 704 * @param ACTION 705 * The code to execute. 706 * 707 * @discussion 708 * No debugging information is logged. 709 */ 710 #define mdns_require_noerr_action_quiet(ERROR, LABEL, ACTION) mdns_require_action_quiet(!(ERROR), LABEL, ACTION) 711 712 /*! 713 * @brief 714 * Returns from the current function if an expression evaluates to false. 715 * 716 * @param EXPRESSION 717 * The expression. 718 */ 719 #define mdns_require_return(EXPRESSION) \ 720 do { \ 721 if (!(EXPRESSION)) { \ 722 return; \ 723 } \ 724 } while (0) 725 726 /*! 727 * @brief 728 * If an expression evaluates to false, executes an action, then returns. 729 * 730 * @param EXPRESSION 731 * The expression. 732 * 733 * @param ACTION 734 * The code to execute. 735 * 736 * @discussion 737 * No debugging information is logged. 738 */ 739 #define mdns_require_return_action(EXPRESSION, ACTION) \ 740 do { \ 741 if (!(EXPRESSION)) { \ 742 { \ 743 ACTION; \ 744 } \ 745 return; \ 746 } \ 747 } while (0) 748 749 /*! 750 * @brief 751 * Returns from the current function with a specified value if an expression evaluates to false. 752 * 753 * @param EXPRESSION 754 * The expression. 755 * 756 * @param VALUE 757 * The return value. 758 */ 759 #define mdns_require_return_value(EXPRESSION, VALUE) \ 760 do { \ 761 if (!(EXPRESSION)) { \ 762 return (VALUE); \ 763 } \ 764 } while (0) 765 766 /*! 767 * @brief 768 * Returns from the current function with a specified return value if an error code is non-zero. 769 * 770 * @param ERROR 771 * The error code. 772 * 773 * @param VALUE 774 * The return value. 775 */ 776 #define mdns_require_noerr_return_value(ERROR, VALUE) mdns_require_return_value(!(ERROR), VALUE) 777 778 /*! 779 * @brief 780 * Assigns a value to a variable if the variable's address isn't NULL. 781 * 782 * @param VARIABLE_ADDR 783 * The variable's address. 784 * 785 * @param VALUE 786 * The value. 787 */ 788 #define mdns_assign(VARIABLE_ADDR, VALUE) \ 789 do { \ 790 if (VARIABLE_ADDR) { \ 791 *(VARIABLE_ADDR) = (VALUE); \ 792 } \ 793 } while (0) 794 795 /*! 796 * @brief 797 * Declares an array of bytes that is meant to be used as explicit padding at the end of a struct. 798 * 799 * @param BYTE_COUNT 800 * The size of the array in number of bytes. 801 * 802 * @discussion 803 * This explicit padding is meant to be used as the final member variable of a struct to eliminate the 804 * -Wpadded warning about a struct's overall size being implicitly padded up to an alignment boundary. 805 * In other words, in place of such implicit padding, use this explicit padding instead. 806 */ 807 #define MDNS_STRUCT_PAD(BYTE_COUNT) \ 808 MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wzero-length-array) \ 809 char _mdns_unused_padding[(BYTE_COUNT)] \ 810 MDNS_CLANG_IGNORE_WARNING_END() 811 812 /*! 813 * @brief 814 * Like MDNS_STRUCT_PAD(), except that the amount of padding is specified for 64-bit and 32-bit platforms. 815 * 816 * @param BYTE_COUNT_64 817 * The amount of padding in number of bytes to use on 64-bit platforms. 818 * 819 * @param BYTE_COUNT_32 820 * The amount of padding in number of bytes to use on 32-bit platforms. 821 * 822 * @discussion 823 * This macro assumes that pointers on 64-bit platforms are eight bytes in size and that pointers on 32-bit 824 * platforms are four bytes in size. 825 * 826 * If the size of a pointer is something other than eight or four bytes, then a compiler error will occur. 827 */ 828 #define MDNS_STRUCT_PAD_64_32(BYTE_COUNT_64, BYTE_COUNT_32) \ 829 MDNS_STRUCT_PAD( \ 830 (sizeof(void *) == 8) ? BYTE_COUNT_64 : \ 831 (sizeof(void *) == 4) ? BYTE_COUNT_32 : -1 \ 832 ) 833 834 /*! 835 * @brief 836 * Compile-time check to ensure that a struct that uses MDNS_STRUCT_PAD() or MDNS_STRUCT_PAD_64_32() hasn't 837 * specified too much padding. 838 * 839 * @param STRUCT_TYPE 840 * The struct type. 841 * 842 * @discussion 843 * There's too much padding if the padding's size is greater than or equal to the struct's alignment 844 * requirement. This is because the point of MDNS_STRUCT_PAD() and MDNS_STRUCT_PAD_64_32() is to explicitly 845 * pad a struct up to a multiple of the struct's alignment requirement. Violating this check would 846 * unnecessarily increase the size of the struct. 847 */ 848 #define MDNS_GENERAL_STRUCT_PAD_CHECK(STRUCT_TYPE) \ 849 mdns_compile_time_check(mdns_sizeof_member(STRUCT_TYPE, _mdns_unused_padding) < _Alignof(STRUCT_TYPE), \ 850 "Padding exceeds alignment of '" # STRUCT_TYPE "', so the amount of padding is excessive.") 851 852 /*! 853 * @brief 854 * Retains a Core Foundation object if the specified object reference is non-NULL. 855 * 856 * @param OBJ 857 * A reference to the object to retain. 858 * 859 * @discussion 860 * The object reference is explicitly compared against NULL to avoid a warning from the Clang analyzer's 861 * osx.NumberObjectConversion checker. See 862 * <https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion-c-c-objc>. 863 */ 864 #define mdns_cf_retain_null_safe(OBJ) \ 865 do { \ 866 if ((OBJ) != NULL) { \ 867 CFRetain((OBJ)); \ 868 } \ 869 } while (0) 870 871 /*! 872 * @brief 873 * Releases the Core Foundation object referenced by a pointer. 874 * 875 * @param OBJ_PTR 876 * The address of the pointer that either references a Core Foundation object or references NULL. 877 * 878 * @discussion 879 * If the pointer contains a non-NULL reference, then the pointer will be set to NULL after releasing the 880 * object. 881 * 882 * The object reference is explicitly compared against NULL to avoid a warning from the Clang analyzer's 883 * osx.NumberObjectConversion checker. See 884 * <https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion-c-c-objc>. 885 */ 886 #define mdns_cf_forget(OBJ_PTR) \ 887 do { \ 888 if (*(OBJ_PTR) != NULL) { \ 889 CFRelease(*(OBJ_PTR)); \ 890 *(OBJ_PTR) = NULL; \ 891 } \ 892 } while (0) 893 894 /*! 895 * @brief 896 * Alternative to the `default` label in a switch statement that covers all enumeration values. 897 * 898 * @discussion 899 * Use `MDNS_COVERED_SWITCH_DEFAULT` instead of `default` to avoid the `-Wcovered-switch-default` warning 900 * in a switch statement that covers all enumeration values. This macro is useful when strict enforcement 901 * of the `-Wswitch-default` warning compels us to include a default label in such switch statements. 902 */ 903 #if MDNS_COMPILER_IS_CLANG() 904 #define MDNS_COVERED_SWITCH_DEFAULT \ 905 MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wcovered-switch-default) \ 906 default \ 907 MDNS_CLANG_IGNORE_WARNING_END() 908 #else 909 #define MDNS_COVERED_SWITCH_DEFAULT default 910 #endif 911 912 /*! 913 * @brief 914 * The static keyword for array parameters for C99 or later. 915 * 916 * @discussion 917 * See <https://en.cppreference.com/w/c/language/operator_other#Function_call>. 918 */ 919 #if MDNS_C_STANDARD_IS_AT_LEAST(C99) 920 #define MDNS_STATIC_ARRAY_PARAM static 921 #else 922 #define MDNS_STATIC_ARRAY_PARAM 923 #endif 924 925 /*! 926 * @brief 927 * The size of a Universally Unique Identifier (UUID) in bytes. 928 * 929 * @discussion 930 * See <https://datatracker.ietf.org/doc/html/rfc4122#section-4.1>. 931 */ 932 #define MDNS_UUID_SIZE 16 933 934 #endif // MDNS_GENERAL_H 935