3939
4040 #ifdef __cplusplus
4141
42- // C++ implementation of the type promotion logic
43- //
44- // Note: the C++ implementation requires the use of C++11 features and the GNU
45- // "##" variadic arg extension. Memfault Compact Logs in C++ require the
46- // compiler flag '--std=gnu++-11' or newer.
42+ // ! C++ type promotion logic
43+ // !
44+ // ! This code defines the MemfaultLogArgPromotionType template structure, responsible for
45+ // ! determining the promotion type of different argument types passed to Memfault's logging
46+ // ! functions. This is important because certain types may need to be promoted to larger or
47+ // ! different types, ensuring consistent handling in logging The code uses C++11 features,
48+ // ! including SFINAE (Substitution Failure Is Not An Error) with std::enable_if, and the GNU ## va
49+ // ! args extension. It assumes the --std=gnu++11 compiler flag or newer.
50+ // !
51+ // ! Structure definition:
52+ // ! 1. Default promotion to int64:
53+ // ! - By default, if no other promotion rule applies, MemfaultLogArgPromotionType promotes the
54+ // ! type to
55+ // ! MEMFAULT_LOG_ARG_PROMOTED_TO_INT64, indicating that any unhandled type defaults to a
56+ // ! 64-bit integer.
57+ // !
58+ // ! 2. Conditional promotion to int32:
59+ // ! - If the size of the type T is less than or equal to 4 bytes (e.g., smaller integer types),
60+ // ! MemfaultLogArgPromotionType promotes the type to MEMFAULT_LOG_ARG_PROMOTED_TO_INT32,
61+ // ! allowing consistent handling for smaller integers.
62+ // !
63+ // ! 3. Specialized promotions:
64+ // ! - Specific types receive unique promotion constants:
65+ // ! - Floating-point types (float, double, long double): Promoted to
66+ // ! MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE.
67+ // ! - String types (char* variants): Promoted to MEMFAULT_LOG_ARG_PROMOTED_TO_STR.
68+ // !
69+ // ! Macro definition:
70+ // ! - _MEMFAULT_LOG_ARG_PROMOTION_TYPE(arg): This macro determines the promotion type of an
71+ // ! argument.
72+ // ! The (arg) + 0 operation is used to enforce integer promotion for non-integer types, helping
73+ // ! the compiler resolve the appropriate type for MemfaultLogArgPromotionType.
4774
4875 #include < type_traits>
4976
@@ -78,22 +105,43 @@ template <>
78105struct MemfaultLogArgPromotionType <const char *>
79106 : std::integral_constant<int , MEMFAULT_LOG_ARG_PROMOTED_TO_STR> { };
80107
81- // When expressing the final type via the template parameter expansion, operate
82- // on (arg) + 0 to force integer promotion
108+ template <>
109+ struct MemfaultLogArgPromotionType <signed char *>
110+ : std::integral_constant<int , MEMFAULT_LOG_ARG_PROMOTED_TO_STR> { };
111+
112+ template <>
113+ struct MemfaultLogArgPromotionType <const signed char *>
114+ : std::integral_constant<int , MEMFAULT_LOG_ARG_PROMOTED_TO_STR> { };
115+
116+ template <>
117+ struct MemfaultLogArgPromotionType <unsigned char *>
118+ : std::integral_constant<int , MEMFAULT_LOG_ARG_PROMOTED_TO_STR> { };
119+
120+ template <>
121+ struct MemfaultLogArgPromotionType <const unsigned char *>
122+ : std::integral_constant<int , MEMFAULT_LOG_ARG_PROMOTED_TO_STR> { };
123+
83124 #define _MEMFAULT_LOG_ARG_PROMOTION_TYPE (arg ) \
84125 MemfaultLogArgPromotionType<decltype ((arg) + 0 )>::value
85126
86127 #else // C Code implementation
87128
88- // ! Preprocessor macro to encode promotion type info about each va_arg in a uint32_t
129+ // ! C type promotion logic
89130 // !
90131 // ! Utilizes the rules around "default argument promotion" (specifically for va_args) defined in
91132 // ! the C specification (http://www.iso-9899.info/wiki/The_Standard) to encode information about
92133 // ! the width of arguments. (For more details see "6.5.2.2 Function calls" in the C11 Spec).
93134 // !
94- // ! In short,
95- // ! - floats are always promoted to doubles
96- // ! - any other type < sizeof(int) is promoted to the width of an int
135+ // ! Promotion Rules:
136+ // ! - Floating-point types (float, double, long double): Promoted to
137+ // ! MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE.
138+ // ! - String types (char* variants): Promoted to MEMFAULT_LOG_ARG_PROMOTED_TO_STR.
139+ // ! - Default case:
140+ // ! - If the size of the argument is less than or equal to the size of an int, the type is
141+ // ! promoted to
142+ // ! MEMFAULT_LOG_ARG_PROMOTED_TO_INT32.
143+ // ! - Otherwise, it defaults to MEMFAULT_LOG_ARG_PROMOTED_TO_INT64, for larger integer types.
144+ // ! - Notably, bool and _BitInt(N) are safely promoted to int by falling into this case.
97145 // !
98146 // ! NOTE 1: We use a special type for "strings" (i.e char *) so we know to encode the value
99147 // ! pointed to (the actual NUL terminated string) in this situation rather than the pointer
@@ -118,15 +166,19 @@ struct MemfaultLogArgPromotionType<const char *>
118166
119167 // clang-format off
120168
121- #define _MEMFAULT_LOG_ARG_PROMOTION_TYPE (arg ) \
122- _Generic ((arg) + 0, \
123- float: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \
124- double: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \
125- long double: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \
126- char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \
127- const char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \
128- default: sizeof((arg) + 0) <= sizeof(int ) ? \
129- MEMFAULT_LOG_ARG_PROMOTED_TO_INT32 : MEMFAULT_LOG_ARG_PROMOTED_TO_INT64)
169+ #define _MEMFAULT_LOG_ARG_PROMOTION_TYPE (arg ) \
170+ _Generic ((arg) + 0, \
171+ float: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \
172+ double: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \
173+ long double: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \
174+ char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \
175+ const char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \
176+ signed char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \
177+ const signed char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \
178+ unsigned char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \
179+ const unsigned char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \
180+ default: sizeof((arg) + 0) <= sizeof(int ) ? \
181+ MEMFAULT_LOG_ARG_PROMOTED_TO_INT32 : MEMFAULT_LOG_ARG_PROMOTED_TO_INT64)
130182
131183 // clang-format on
132184
0 commit comments