inc/macro_utils/macro_utils.h (327 lines of code) (raw):

// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #ifndef MACRO_UTILS_H #define MACRO_UTILS_H #include "macro_utils/macro_utils_generated.h" #ifdef __cplusplus #include <cstring> #include <cstddef> #include <ctime> #include <cinttypes> extern "C" { #else #include <string.h> #include <stddef.h> #include <time.h> #include <inttypes.h> #endif #if (defined OPTIMIZE_RETURN_CODES) #define MU_FAILURE 1 #else #define MU_FAILURE __LINE__ #endif #ifdef _MSC_VER #define MU_UNUSED_VAR __pragma(warning(suppress:4189)) #elif __GNUC__ #define MU_UNUSED_VAR __attribute__((unused)) #else // Unknown compiler #endif /*"pointer or NULL" macro - because when printf-ing arguments NULL is not valid for %s (section 7.1.4 of C11 standard) */ #define MU_P_OR_NULL(p) (((p)!=NULL)?(p):"NULL") #define MU_WP_OR_NULL(p) (((p)!=NULL)?(p):L"NULL") #define MU_TOSTRING_(x) #x #define MU_TOSTRING(x) MU_TOSTRING_(x) #define MU_TOWSTRING_(x) L ## #x #define MU_TOWSTRING(x) MU_TOWSTRING_(x) /*macro that expands to a printf format specifier that can be used to nice print filesizes. It produces output like 45 B (45) or 1.9 GB (2040109488) or 2 KB (2050).*/ #define PRI_KB "" PRIu64 "%s%." PRIu64 " %s" #define PRI_1KB (1024) #define PRI_1MB (PRI_1KB*PRI_1KB) #define PRI_1GB (PRI_1MB*PRI_1KB) /*below macro produces the integer part of the value (x) in KB, MB or GB*/ #define KB_VALUE_INT(x) ( \ (x)>=PRI_1GB? \ ((x)/PRI_1GB) \ : \ (x)>=PRI_1MB? \ ((x)/PRI_1MB) \ : \ (x)>=PRI_1KB? \ ((x)/PRI_1KB) \ : \ (x) \ ) /*below macro produces the first digit of the fractional part of the value (x) in KB, MB or GB*/ #define KB_FIRST_FRACTIONAL_DIGIT(x) ( \ (x)>=PRI_1GB? \ ((((x)*10)/PRI_1GB)%10) \ : \ (x)>=PRI_1MB? \ ((((x)*10)/PRI_1MB)%10) \ : \ (x)>=PRI_1KB? \ ((((x)*10)/PRI_1KB)%10) \ : \ 0 \ ) /*this macro produces the fractional dot IF the first fractional digit is not 0*/ #define KB_FRACTIONAL_DOT(x) ((KB_FIRST_FRACTIONAL_DIGIT(x)>0)?".":"") /*below macro produces the unit for measurement for (x)*/ #define KB_UNIT(x) ( \ (x)>=PRI_1GB? \ "GB" \ : \ (x)>=PRI_1MB? \ "MB" \ : \ (x)>=PRI_1KB? \ "KB" \ : \ "B" \ ) /*KB_VALUE is the counterpart of PRI_KB.*/ #define KB_VALUE(x) KB_VALUE_INT(x), KB_FRACTIONAL_DOT(x), KB_FIRST_FRACTIONAL_DIGIT(x), KB_UNIT(x) #define MU_TRIGGER_PARENTHESIS(...) , #if defined(_MSC_VER) && (_MSC_VER<1920) #define MU_LPAREN ( #endif #define MU_C2_(x,y) x##y #define MU_C2(x,y) MU_C2_(x,y) /*the following macros (MU_C2A. MU_C2B, MU_C2C) exist only to have a different name than MU_C2 but provide the same functionality*/ /*this is needed to avoid recursion of macro expansions in certain cases*/ #define MU_PASTEA_(x,y) x##y #define MU_C2A(x,y) MU_PASTEA_(x,y) #define MU_PASTEB_(x,y) x##y #define MU_C2B(x,y) MU_PASTEB_(x,y) #define MU_PASTEC_(x,y) x##y #define MU_C2C(x,y) MU_PASTEC_(x,y) #define MU_C3(x,y,z) MU_C2(x, MU_C2(y,z)) #define MU_C4(x,y,z, u) MU_C2(MU_C2(x,y), MU_C2(z,u)) #define MU_C5(x,y,z,u, v) MU_C2(MU_C4(x,y, z, u), v) #define MU_C1_(x) x #define MU_C1(x) MU_C1_(x) #define MU_C2STRING(x,y) x y #define MU_C3STRING(x,y,z) x y z #define MU_C4STRING(x,y,z,u) x y z u #define MU_C5STRING(x,y,z,u,v) x y z u v /* we need some sort of macro that does: MU_IF(0, "true", "false") => "false" MU_IF(1, "true", "false") => "true" MU_IF(X, "true", "false") => "true" */ #define MU_INTERNALIF(x) MU_INTERNALIF##x #define MU_INTERNALIF0 #define MU_ISZERO(x) MU_COUNT_ARG(MU_INTERNALIF(x)) #define MU_IF(condition, trueBranch, falseBranch) MU_C2(MU_IF,MU_ISZERO(condition))(trueBranch, falseBranch) #define MU_IF0(trueBranch, falseBranch) falseBranch #define MU_IF1(trueBranch, falseBranch) trueBranch /*the following macro want to eat empty arguments from a list */ /*examples: */ /*MU_EAT_EMPTY_ARGS(, , X) expands to X */ #define MU_EXPAND_TO_NOTHING(arg) #define MU_EAT_EMPTY_ARG(arg_count, arg) MU_IF(MU_ISEMPTY(arg),,arg) MU_IF(MU_ISEMPTY(arg),MU_EXPAND_TO_NOTHING,MU_IFCOMMALOGIC)(MU_DEC(arg_count)) #define MU_EAT_EMPTY_ARGS(...) MU_FOR_EACH_1_COUNTED(MU_EAT_EMPTY_ARG, __VA_ARGS__) #define MU_DEFINE_ENUMERATION_CONSTANT(x) x, #define MU_DEFINE_METADATA_ENUMERATION_VALUE(x) MU_C2(enum_value_typedef_, x), #define MU_DEFINE_ENUM_WITHOUT_INVALID_TYPEDEF(enumName, ... ) \ typedef enum MU_C2(enumName, _TAG) { MU_FOR_EACH_1(MU_DEFINE_ENUMERATION_CONSTANT, __VA_ARGS__)} enumName; #define MU_DECLARE_ENUM_TO_STRING(enumName, ...) \ const char* MU_C3(MU_, enumName, _ToString)(enumName value); #define MU_DEFINE_ENUM_WITHOUT_INVALID_TO_STRING(enumName, ... ) \ typedef enum MU_C3(enum_value_metadata_, enumName, _TAG) { MU_FOR_EACH_1(MU_DEFINE_METADATA_ENUMERATION_VALUE, __VA_ARGS__) MU_C3(enum_value_metadata_, enumName, _VALUE_COUNT) = MU_COUNT_ARG(__VA_ARGS__)} MU_C2(enum_value_metadata_, enumName); \ MU_DECLARE_ENUM_TO_STRING(enumName, __VA_ARGS__) \ /*MU_DEFINE_ENUM_WITHOUT_INVALID goes to header*/ #define MU_DEFINE_ENUM_WITHOUT_INVALID(enumName, ...) \ MU_DEFINE_ENUM_WITHOUT_INVALID_TYPEDEF(enumName, __VA_ARGS__) \ MU_DEFINE_ENUM_WITHOUT_INVALID_TO_STRING(enumName, __VA_ARGS__) // this macro is a wrapper on top of MU_DEFINE_ENUM_WITHOUT_INVALID, adding an _INVALID value as the first enum value in the enum #define MU_DEFINE_ENUM(enumName, ...) \ MU_DEFINE_ENUM_WITHOUT_INVALID(enumName, MU_C2(enumName, _INVALID), __VA_ARGS__) #define MU_INTERNAL_DEFINE_ENUM_VAR(enumName, enumValue) \ static enumName MU_C2(my_, enumValue); #define MU_INTERNAL_ASSIGN_ENUM_VALUE(argCount, enumValue) \ switch (MU_C2(enum_value_has_equal_, argCount)) \ { \ default: \ case MU_ENUM_VALUE_CONTAINS_EQUAL_UNKNOWN: \ if (strchr(MU_TOSTRING(MU_C2(my_, enumValue)), '=') != NULL) \ { \ MU_C2(enum_value_has_equal_, argCount) = MU_ENUM_VALUE_CONTAINS_EQUAL_YES; \ has_equal = 1; \ } \ else \ { \ MU_C2(enum_value_has_equal_, argCount) = MU_ENUM_VALUE_CONTAINS_EQUAL_NO; \ has_equal = 0; \ } \ break; \ case MU_ENUM_VALUE_CONTAINS_EQUAL_YES: \ has_equal = 1; \ break; \ case MU_ENUM_VALUE_CONTAINS_EQUAL_NO: \ has_equal = 0; \ break; \ } \ if ((MU_C2(my_, enumValue)) == 0) \ { \ /* it is zero, either because not filled in or because it was explicit zero */ \ /* if it has an equal it cannot be implicit */ \ if (has_equal) \ { \ /* explicit zero */ \ current_enum_value = 0; \ } \ else \ { \ current_enum_value = previous_enum_value + 1; \ } \ } \ else \ { \ /* non zero value, thus it was filled in */ \ current_enum_value = MU_C2(my_, enumValue); \ } \ previous_enum_value = current_enum_value; \ if (current_enum_value == value) \ { \ static char result [] = MU_TOSTRING(enumValue); \ static int visited; /*initialized to 0 by "static"*/ \ if(visited == 0) \ { \ result[strspn(result, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_")] = '\0'; \ visited = 1; \ } \ return result; \ } #define MU_DEFINE_ENUMERATION_CONSTANT_AS_WIDESTRING(x) MU_C2(L, MU_TOSTRING(x)) , #define MU_DEFINE_ENUMERATION_CONSTANT_AS_STRING(x) MU_TOSTRING(x) , typedef enum MU_ENUM_VALUE_CONTAINS_EQUAL_TAG { MU_ENUM_VALUE_CONTAINS_EQUAL_UNKNOWN, MU_ENUM_VALUE_CONTAINS_EQUAL_YES, MU_ENUM_VALUE_CONTAINS_EQUAL_NO } MU_ENUM_VALUE_CONTAINS_EQUAL; // variable definition indicating if an enum value label contains or not equal #define MU_INTERNAL_DEFINE_ENUM_HAS_EQ_VAR(argCount, enumValue) \ static MU_ENUM_VALUE_CONTAINS_EQUAL MU_C2(enum_value_has_equal_, argCount) = MU_ENUM_VALUE_CONTAINS_EQUAL_UNKNOWN; \ // This is the bulk of the _ToString for an enum // The algorithm is: // - Go through each enum value // - Determine if it contains an equal by searching '=' in the enum value string // This value is cached. Threading is not taken into account, if a dirty read happens and an unknown value is obtained // the code will simply take the worse case and take a performance hit in recomputing whether an equal is in the string // - Determine the actual value of the enum value that we need to compare against: // - Have a fake variable that is either initialized because there is an equal in the enum label or it is initialized to 0 // because the fake variables are static // - If the enum value contains an equal then take as current enum value whatever is in the fake variable // - If the enum value does not contain an equal then compute the current enum value as the previous enum value plus 1 // - If the currently computed enum value matches the value requested to be stringified by the user then return the string to the user #define MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID_COMMON_CODE(enumName, ...) \ { \ int has_equal = 0; \ MU_FOR_EACH_1_KEEP_1(MU_INTERNAL_DEFINE_ENUM_VAR, enumName, __VA_ARGS__) \ MU_FOR_EACH_1_COUNTED(MU_INTERNAL_DEFINE_ENUM_HAS_EQ_VAR, __VA_ARGS__) \ int current_enum_value = -1; \ int previous_enum_value = -1; \ MU_FOR_EACH_1_COUNTED(MU_INTERNAL_ASSIGN_ENUM_VALUE, __VA_ARGS__) \ return "UNKNOWN"; \ } /*MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID goes to .c*/ #define MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(enumName, ...) \ const char* MU_C3(MU_, enumName, _ToString)(enumName value) \ MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID_COMMON_CODE(enumName, __VA_ARGS__) // this macro is a wrapper on top of MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID, adding an _INVALID value as the first enum value in the enum #define MU_DEFINE_ENUM_STRINGS(enumName, ...) \ MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(enumName, MU_C2(enumName, _INVALID), __VA_ARGS__) #define MU_DEFINE_LOCAL_ENUM_WITHOUT_INVALID(enumName, ...) \ typedef enum MU_C2(enumName, _TAG) { MU_FOR_EACH_1(MU_DEFINE_ENUMERATION_CONSTANT, __VA_ARGS__)} enumName; \ static const char* MU_C3(MU_, enumName, _ToString)(enumName value) \ MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID_COMMON_CODE(enumName, __VA_ARGS__) // this macro is a wrapper on top of MU_DEFINE_LOCAL_ENUM_WITHOUT_INVALID, adding an _INVALID value as the first enum value in the enum #define MU_DEFINE_LOCAL_ENUM(enumName, ...) \ MU_DEFINE_LOCAL_ENUM_WITHOUT_INVALID(enumName, MU_C2(enumName, _INVALID), __VA_ARGS__) // this macro returns the number of enum values (taking into account that an invalid value is generated) #define MU_ENUM_VALUE_COUNT(...) (MU_COUNT_ARG(__VA_ARGS__) + 1) #define MU_ENUM_VALUE_COUNT_WITHOUT_INVALID(...) (MU_COUNT_ARG(__VA_ARGS__)) #define MU_ENUM_TO_STRING(enumName, value) \ MU_C3(MU_, enumName, _ToString)(value) #define MU_STRING_TO_ENUM(stringValue, enumName, addressOfEnumVariable) MU_C2(enumName, _FromString)(stringValue, addressOfEnumVariable) #define MU_EMPTY() #define MACRO_UTILS_DELAY(id) id MU_EMPTY MU_LPAREN ) #define MU_DECLARE_ENUM_STRINGS(enumIdentifier, ...) extern const char* MU_C3(MU_, enumIdentifier,_ToString)(enumIdentifier enumerationConstant); /*PRI_BOOL is the format specifier that prints a bool/_Bool*/ #define PRI_BOOL "s (%d)" /*MU_BOOL_VALUE is the counterpart of PRI_BOOL*/ #define MU_FALSE_STRING "false" #define MU_TRUE_STRING "true" #define MU_BOOL_VALUE(v) &(MU_FALSE_STRING "\0" MU_TRUE_STRING[((!!(v))*sizeof(MU_FALSE_STRING))]), (v) /*PRI_TIME_T is to be used with a time_t variable - it produces strings such as "Tue May 4 14:42:17 2021". As per 7.27.3.1 The asctime function of C11 it seems that limiting the output to 24 characters should be safe.*/ #define PRI_TIME_T ".24s" /*TIME_T_VALUE is the counterpar of PRI_TIME_T in a printf/LogError instruction*/ #define TIME_T_VALUE(t) ctime(&t) /*PRI_MU_ENUM and MU_ENUM_VALUE work together as printf format specifier/argument. e.g: printf("enumValue was=%" PRI_MU_ENUM "\n", MU_ENUM_TO_STRING(enumIdentifier, enumValue));*/ #define PRI_MU_ENUM "s%s (%d)" #define MU_ENUM_VALUE(enumIdentifier, value) "", MU_ENUM_TO_STRING(enumIdentifier, (value)), (int)(value) #define CONVERT_ENUM_VALUE(from, to) \ case (from): \ *enum_value_to = (to); \ return 0; \ #define MU_CONVERT_ENUM(ENUM_TYPE_FROM, ENUM_TYPE_TO) \ MU_C4(convert_enum_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO) #define MU_CONVERT_ENUM_WITH_DEFAULT(ENUM_TYPE_FROM, ENUM_TYPE_TO) MU_C5(convert_enum_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _with_default) // This macro declares an enum conversion function that translates a value from one enum to another #define MU_DECLARE_CONVERT_ENUM(ENUM_TYPE_FROM, ENUM_TYPE_TO) \ int MU_CONVERT_ENUM(ENUM_TYPE_FROM, ENUM_TYPE_TO)(ENUM_TYPE_FROM enum_value_from, ENUM_TYPE_TO* enum_value_to); // This macro declares an enum conversion function that translates a value from one enum to another with a default value #define MU_DECLARE_CONVERT_ENUM_WITH_DEFAULT(ENUM_TYPE_FROM, ENUM_TYPE_TO) \ ENUM_TYPE_TO MU_CONVERT_ENUM_WITH_DEFAULT(ENUM_TYPE_FROM, ENUM_TYPE_TO)(ENUM_TYPE_FROM from); // This macro defines an enum conversion function that translates a value from one enum to another // It is implemented as a switch statement. This allows for catching multiple from values being in the list also #define MU_DEFINE_CONVERT_ENUM_WITHOUT_INVALID(ENUM_TYPE_FROM, ENUM_TYPE_TO, ...) \ int MU_CONVERT_ENUM(ENUM_TYPE_FROM, ENUM_TYPE_TO)(ENUM_TYPE_FROM enum_value_from, ENUM_TYPE_TO* enum_value_to) \ { \ switch (enum_value_from) \ { \ default: \ break; \ MU_FOR_EACH_2(CONVERT_ENUM_VALUE, __VA_ARGS__) \ } \ return MU_FAILURE; \ } // This macro simply adds the conversion of the "well known" by now _INVALID enum value #define MU_DEFINE_CONVERT_ENUM_WITHOUT_VALIDATION(ENUM_TYPE_FROM, ENUM_TYPE_TO, ...) \ MU_DEFINE_CONVERT_ENUM_WITHOUT_INVALID(ENUM_TYPE_FROM, ENUM_TYPE_TO, MU_C2(ENUM_TYPE_FROM, _INVALID), MU_C2(ENUM_TYPE_TO, _INVALID), __VA_ARGS__) #define CONSTRUCT_FROM_FAKE_ENUM(from_value, to_value) \ MU_C5(fake_from_, from_value, _, to_value, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51), #define CONSTRUCT_FIELD_FROM_FAKE_ENUM(from_value, to_value) \ int MU_C5(fake_from_, from_value, _, to_value, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51)[(MU_C2(enum_value_typedef_, from_value) == MU_C2(enum_value_typedef_, from_value)) ? 1 : 0]; // This macro adds validation of the fact that the from values count in the list match the count of values in the from enum type // (as opposed to the macro MU_DEFINE_CONVERT_ENUM_WITHOUT_VALIDATION). It does that by having a special fake enum where all the "from" values are enumerated // The count check is done by having a zero sized array in a struct if the counts do not match #define MU_DEFINE_CONVERT_ENUM(ENUM_TYPE_FROM, ENUM_TYPE_TO, ...) \ /* This forces matching the values in from to enum values generated with MU_DEFINE_ENUM */ \ typedef struct MU_C5(fake_check_enum_values_exist_, ENUM_TYPE_FROM,_, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51_TAG) \ { \ MU_FOR_EACH_2(CONSTRUCT_FIELD_FROM_FAKE_ENUM, __VA_ARGS__) \ } MU_C5(fake_check_enum_values_exist_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51); \ /* Have an enum which has all the "from" values */ \ /* This enum will have a count */ \ typedef enum MU_C5(fake_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51_TAG) \ { \ MU_FOR_EACH_2(CONSTRUCT_FROM_FAKE_ENUM, __VA_ARGS__) \ MU_C5(fake_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51_count) \ } MU_C5(fake_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51); \ /* "Compare" the count of the from enum values with the count of the values in "ENUM_TYPE_FROM" */ \ typedef struct MU_C5(fake_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51_struct_TAG) \ { \ int MU_C5(fake_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51_count_test_array)[((MU_C5(fake_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51_count) + 1) == MU_C3(enum_value_metadata_, ENUM_TYPE_FROM, _VALUE_COUNT)) ? 1 : 0]; \ int dummy; \ } MU_C5(fake_, ENUM_TYPE_FROM, _, ENUM_TYPE_TO, _C0FB3709_EDE8_4288_8F70_FDDB5D8D7A51_struct); \ MU_DEFINE_CONVERT_ENUM_WITHOUT_VALIDATION(ENUM_TYPE_FROM, ENUM_TYPE_TO, __VA_ARGS__) // Internal macro which adds a wrapper around the convert enum which will handle the return code and translate to a default #define MU_DEFINE_CONVERT_ENUM_WITH_DEFAULT_INTERNAL(ERROR_LOGGING_FUNCTION, ENUM_TYPE_FROM, ENUM_TYPE_TO, DEFAULT, ...) \ ENUM_TYPE_TO MU_CONVERT_ENUM_WITH_DEFAULT(ENUM_TYPE_FROM, ENUM_TYPE_TO)(ENUM_TYPE_FROM from) \ { \ ENUM_TYPE_TO result; \ if (MU_CONVERT_ENUM(ENUM_TYPE_FROM, ENUM_TYPE_TO)(from, &result) != 0) \ { \ ERROR_LOGGING_FUNCTION("Encountered unknown enum value %" PRI_MU_ENUM ", treat as %" PRI_MU_ENUM, \ MU_ENUM_VALUE(ENUM_TYPE_FROM, from), MU_ENUM_VALUE(ENUM_TYPE_TO, DEFAULT)); \ result = DEFAULT; \ } \ return result; \ } // Defines a conversion where unknown values are converted to DEFAULT #define MU_DEFINE_CONVERT_ENUM_WITH_DEFAULT(ERROR_LOGGING_FUNCTION, ENUM_TYPE_FROM, ENUM_TYPE_TO, DEFAULT, ...) \ MU_DEFINE_CONVERT_ENUM(ENUM_TYPE_FROM, ENUM_TYPE_TO, __VA_ARGS__) \ MU_DEFINE_CONVERT_ENUM_WITH_DEFAULT_INTERNAL(ERROR_LOGGING_FUNCTION, ENUM_TYPE_FROM, ENUM_TYPE_TO, DEFAULT, __VA_ARGS__) \ // Defines a conversion where unknown values are converted to DEFAULT // using the without validation version so enum values can be skipped #define MU_DEFINE_CONVERT_ENUM_WITH_DEFAULT_WITHOUT_VALIDATION(ERROR_LOGGING_FUNCTION, ENUM_TYPE_FROM, ENUM_TYPE_TO, DEFAULT, ...) \ MU_DEFINE_CONVERT_ENUM_WITHOUT_VALIDATION(ENUM_TYPE_FROM, ENUM_TYPE_TO, __VA_ARGS__) \ MU_DEFINE_CONVERT_ENUM_WITH_DEFAULT_INTERNAL(ERROR_LOGGING_FUNCTION, ENUM_TYPE_FROM, ENUM_TYPE_TO, DEFAULT, __VA_ARGS__) \ // Defines a conversion where unknown values are converted to DEFAULT and the _INVALID value (0) is treated as DEFAULT #define MU_DEFINE_CONVERT_ENUM_WITH_DEFAULT_WITHOUT_INVALID(ERROR_LOGGING_FUNCTION, ENUM_TYPE_FROM, ENUM_TYPE_TO, DEFAULT, ...) \ MU_DEFINE_CONVERT_ENUM_WITHOUT_INVALID(ENUM_TYPE_FROM, ENUM_TYPE_TO, __VA_ARGS__) \ MU_DEFINE_CONVERT_ENUM_WITH_DEFAULT_INTERNAL(ERROR_LOGGING_FUNCTION, ENUM_TYPE_FROM, ENUM_TYPE_TO, DEFAULT, __VA_ARGS__) \ #define MU_DEFINE_STRUCT_FIELD(fieldType, fieldName) fieldType fieldName; /*MU_DEFINE_STRUCT allows creating a struct typedef based on a list of fields*/ #define MU_DEFINE_STRUCT(structName, ...) typedef struct MU_C2(structName, _TAG) { MU_FOR_EACH_2(MU_DEFINE_STRUCT_FIELD, __VA_ARGS__)} structName; // this macro allows counting of elements in an array #define MU_COUNT_ARRAY_ITEMS(A) (sizeof(A)/sizeof((A)[0])) #ifdef _MSC_VER #define MU_SUPPRESS_WARNING(warn_no) \ __pragma(warning(push)) \ __pragma(warning(disable: warn_no)) /*note: warn_no is not used, but helps in code to have it written down*/ #define MU_UNSUPPRESS_WARNING(warn_no) \ __pragma(warning(pop)) #else #define MU_SUPPRESS_WARNING(warn_no) #define MU_UNSUPPRESS_WARNING(warn_no) #endif /*MU_EXPAND and MU_NOEXPAND are macros that work in pairs. Here's a typical usage of MU_IF: MU_IF(SOMETHING, TRUEBRANCH(), FALSEBRANCH())*/ /*if FALSEBRANCH() or TRUEBRANCH() expand to something containing a comma, then MU_IF is unhappy because MU_IF expects exactly 3 token.*/ /*the idea is to hide/grab TRUEBRANCH(), FALSEBRANCH(), stick in MU_NOEXPAND. By the way, MU_NOEXPAND is just a token, not even a macro*/ /*so MU_IF actually expands to MU_NOEXPAND(whaveverTRUEBRANCH/FALSEBRANCHexpandedto). to remove MU_NOEXPAND , MU_EXPAND shall be used*/ /*MU_EXPAND actually concatenates with its argument: MU_EXPAND(x)=> MU_EXPAND_x. it so happens that there*/ /*is a macro called MU_EXPAND_MU_NOEXPAND(...) that expands to __VA_ARGS__, thus stealing TRUE/FALSE branch from under MU_IF*/ /*according to https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019 this is where VS 2019 starts (1920)*/ /*this is needed because there is NO /experimental:preprocessor or /Zc:preprocessor for VS 2017, it only exists for C++ compiler... apparently*/ /*and there's an absolute need to compile with 2017 (read: vcredist 2019 not being available in the environment where the resulting compiled code is run)*/ #if defined(_MSC_VER) && (_MSC_VER < 1920 ) /*for anything < VS 2019*/ #define MU_NOEXPAND(...) __VA_ARGS__ #define MU_EXPAND(...) __VA_ARGS__ #else #define MU_C2_MU_EXPAND_(x,y) x##y #define MU_C2_MU_EXPAND(x,y) MU_C2_MU_EXPAND_(x,y) #define MU_EXPAND_MU_NOEXPAND(...) __VA_ARGS__ #define MU_EXPAND(x) MU_C2_MU_EXPAND(MU_EXPAND_,x) #endif #define MU_IS_VALUE_EQUAL_ONE_OF_VALUES(VALUE, ONE_OF_VALUES) +((VALUE)==(ONE_OF_VALUES)) /*evaluates to 0 or 1*/ #define MU_IS_NONE_OF(VALUE, ...) ( MU_FOR_EACH_1_KEEP_1(MU_IS_VALUE_EQUAL_ONE_OF_VALUES, (VALUE), __VA_ARGS__) == 0) #define MU_IS_NONE_OF_EXPRESSION_BUILDER(N, ...) MU_IS_NONE_OF(N, __VA_ARGS__) ? (N) : #define MU_DIFFERENT(...) (MU_DO(MU_COUNT_ARG(__VA_ARGS__), MU_IS_NONE_OF_EXPRESSION_BUILDER, __VA_ARGS__) (-1) ) /*-1 is the last ?: ternary third operator, and it is never an output of this macro*/ #define MU_STATIC_ASSERT_EX(CONDITION, LINE) typedef int MU_UNUSED_VAR MU_C3(assertion_line_, LINE, _failed)[(CONDITION) ? 1 : -1]; #define MU_STATIC_ASSERT(CONDITION) MU_STATIC_ASSERT_EX(CONDITION, __LINE__) #if defined _MSC_VER #define MU_FUNCDNAME __FUNCDNAME__ #else #define MU_FUNCDNAME __func__ #endif #ifdef __cplusplus } #endif #endif /*MACRO_UTILS_H*/