inc/c_util/flags_to_string.h (76 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 FLAGS_TO_STRING_H #define FLAGS_TO_STRING_H #include <inttypes.h> #include <stdbool.h> #include "macro_utils/macro_utils.h" #include "c_logging/logger.h" #include "c_pal/string_utils.h" /*introduce a name for function that can be called. The name is based on "X", User code might look like FLAGS_TO_STRING(ISC_REQ)(value)*/ #define FLAGS_TO_STRING(X) MU_C2(FLAGS_TO_STRING_,X) /*this macro declares a function to be used in a header*/ #define FLAGS_TO_STRING_DECLARE_FUNCTION(X) MOCKABLE_FUNCTION(, char*, FLAGS_TO_STRING(X), uint32_t, argument) /*this macro defined a function to be used in a .c file. ... is a list of pairs of FLAG_VALUE and FLAG_NAME. FLAG_VALUE is a number and FLAG_NAME is a string*/ #define FLAGS_TO_STRING_DEFINE_FUNCTION(X, ...) \ char* FLAGS_TO_STRING(X)(uint32_t argument) \ { \ char* result; \ \ bool firstFlag = true; \ \ /*Codes_SRS_FLAGS_TO_STRING_02_001: [ For each of the known flags FLAGS_TO_STRING(X) shall define 2 variables: separator_N and value_N both of them of type const char*. N is a decreasing number (all the way to 1) for every of the predefined flags. ]*/ \ /*Codes_SRS_FLAGS_TO_STRING_02_002: [ separator_N shall contain either "" (empty string) or " | ", depending on whether this is the first known flag or not. ]*/ \ /*Codes_SRS_FLAGS_TO_STRING_02_003: [ If the flag is set then value_N shall contain the stringification of the flag. ]*/ \ /*Codes_SRS_FLAGS_TO_STRING_02_004: [ If the flag is not set then value_N shall contain "" (empty string). ]*/ \ /*Codes_SRS_FLAGS_TO_STRING_02_005: [ FLAGS_TO_STRING(X) shall reset (set to 0) all the used known flags in argument. ]*/ \ MU_FOR_EACH_2_COUNTED(MAKE_SEPARATOR_AND_VALUE, __VA_ARGS__); \ \ if (argument != 0) \ { \ \ /*Codes_SRS_FLAGS_TO_STRING_02_006: [ If following the reset of the known flags, there are still bits set in argument, then FLAGS_TO_STRING(X) shall prepare a string using the format "%s%s...%sUNKNOWN_FLAG(%#.8" PRIx32 ")", where each pair of %s%s corresponds to a pair of separator_N/value_N; the last %s corresponds to either "" or " | " depeding on argument not containing or containing any known flags; %PRIx32 corresponds to the remaining (unknown) flags; ]*/ \ const char* unknownFlag_separator = firstFlag ? (firstFlag = false, "") : " | "; \ \ result = sprintf_char(MU_FOR_EACH_2_COUNTED(MAKE_SEPARATOR_AND_VALUE_FORMAT, __VA_ARGS__) "%sUNKNOWN_FLAG(%#.8" PRIx32 ")", \ MU_FOR_EACH_2_COUNTED(USE_SEPARATOR_AND_VALUE, __VA_ARGS__), \ unknownFlag_separator, \ argument); \ if (result == NULL) \ { \ /*Codes_SRS_FLAGS_TO_STRING_02_009: [ If there are any failures then FLAGS_TO_STRING(X) shall fails and return NULL. ]*/ \ LogError("failure in sprintf_char(MU_FOR_EACH_2_COUNTED(MAKE_SEPARATOR_AND_VALUE_FORMAT, __VA_ARGS__) \"%%sUNKNOWN_FLAG(%%#.8\" PRIx32 \")\", MU_FOR_EACH_2_COUNTED(USE_SEPARATOR_AND_VALUE, __VA_ARGS__), unknownFlag_separator=%s, argument=%" PRIu32 ");", \ unknownFlag_separator, argument); \ /*return as is*/ \ } \ else \ { \ /*Codes_SRS_FLAGS_TO_STRING_02_008: [ FLAGS_TO_STRING(X) shall succeed and return a non-NULL string. ]*/ \ /*return as is*/ \ } \ \ } \ else \ { \ /*Codes_SRS_FLAGS_TO_STRING_02_007: [ If following the reset of the known flags, there are no bits set in argument, then FLAGS_TO_STRING(X) shall prepare a string using the format "%s%s...%s", where each pair of %s%s corresponds to a pair of separator_N/value_N; ]*/ \ result = sprintf_char(MU_FOR_EACH_2_COUNTED(MAKE_SEPARATOR_AND_VALUE_FORMAT, __VA_ARGS__), \ MU_FOR_EACH_2_COUNTED(USE_SEPARATOR_AND_VALUE, __VA_ARGS__)); \ if (result == NULL) \ { \ /*Codes_SRS_FLAGS_TO_STRING_02_009: [ If there are any failures then FLAGS_TO_STRING(X) shall fails and return NULL. ]*/ \ LogError("failure in sprintf_char(MU_FOR_EACH_2_COUNTED(MAKE_SEPARATOR_AND_VALUE_FORMAT, __VA_ARGS__), MU_FOR_EACH_2_COUNTED(USE_SEPARATOR_AND_VALUE, __VA_ARGS__));"); \ /*return as is*/ \ } \ else \ { \ /*Codes_SRS_FLAGS_TO_STRING_02_008: [ FLAGS_TO_STRING(X) shall succeed and return a non-NULL string. ]*/ \ /*return as is*/ \ } \ \ } \ return result; \ } \ /* END OF PUBLIC EXPOSED MACROS */ /*internal helper macro - comma operator used to generate separators and value for each of the flags. Note: ?: operator are used here abundantly*/ #define MAKE_SEPARATOR_AND_VALUE(N, FLAG, STRING) \ const char* MU_C2(separator_, MU_DIV2(N)) = ((argument & FLAG) == FLAG) ? (firstFlag ? (firstFlag = false, "") : " | ") : ""; \ const char* MU_C2(value_, MU_DIV2(N)) = ((argument & FLAG) == FLAG) ? (argument&=~FLAG, STRING) : ""; \ /*internal helper macro*/ #define USE_SEPARATOR_AND_VALUE(N, FLAG, STRING) \ MU_C2(separator_, MU_DIV2(N)), \ MU_C2(value_, MU_DIV2(N)) MU_IFCOMMALOGIC(MU_DEC(MU_DEC(N))) \ /*internal helper macro*/ #define MAKE_SEPARATOR_AND_VALUE_FORMAT(N, FLAG, STRING) \ "%s%s" #endif /*FLAGS_TO_STRING_H*/