From 3df2408ffb373fc67c05f490d80d5c2f2962d0fe Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sun, 10 Jan 2021 09:26:27 +0100 Subject: [PATCH] Add macro M_CSTR --- README.md | 11 +++++++ m-core.h | 73 ++++++++++++++++++++++++++++++++++++++++++++-- tests/test-mcore.c | 11 +++++++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3515c132..91eb1a72 100644 --- a/README.md +++ b/README.md @@ -5700,6 +5700,17 @@ If it cannot, the compilation failed. Assuming 'ptr' is a pointer to a fieldType object that is stored within a structure of type 'type' at the position 'field', it returns a pointer to the structure. +##### M\_CSTR(format, ...) + +Return a constant string constructed based on the printf-liked formated string +and its arguments. + +The string is constructed at run time and uses a temporary space on the stack. +If the constructed string is longer than M\_USE\_CSTR\_ALLOC (default 256), +the string is truncated. Example: + + strcmp( M_CSTR("Len=%d", 17) , "Len=17" ) == 0 + #### HASH Functions diff --git a/m-core.h b/m-core.h index 359819aa..3584a3f2 100644 --- a/m-core.h +++ b/m-core.h @@ -29,7 +29,7 @@ #include #include #include - +#include #include #include #include @@ -1971,7 +1971,7 @@ M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ldouble, long double, strtold, ) * a null pointer constant, the returned type depends if the **other** * expression is itself a null pointer constant or not. */ -#define M_CONDITIONAL(cond, true_expr, false_expr) \ +#define M_CONDITIONAL(cond, true_expr, false_expr) \ _Generic(1 ? (float *) 0 : (void *)(intptr_t) (cond), float *: false_expr, void *: true_expr) /* Return the minimum between x and y (computed in compile time) */ @@ -2054,6 +2054,75 @@ M_PARSE_DEFAULT_TYPE_DEF(m_core_parse_ldouble, long double, strtold, ) #endif +/* Define the allocation of the temporary string used by M_CSTR + Default is 256 bytes (so 255 characters excluding the final null char). + It can be overriden by users if needed. +*/ +#ifndef M_USE_CSTR_ALLOC +#define M_USE_CSTR_ALLOC 256 +#endif + +/* Define M_CSTR for C++ & C */ +#if defined(__cplusplus) +namespace m_lib { + template + struct m_char_array { + char str[N]; + inline m_char_array(const char *format, ...) + { + va_list args; + va_start (args, format); + int ret = vsnprintf(str, N, format, args); + (void) ret; + va_end (args); + } + inline const char *m_get(void) + { + return str; + } + }; +} + +/* Return a constant string constructed based on the printf-liked formated string + and its arguments. + The string is constructed at run time and uses a temporary space on the stack. + If the constructed string is longer than M_USE_CSTR_ALLOC (default 256), + the string is truncated. + Example: + strlen( M_CSTR("Len=%d", 17) ) == 6 + NOTE: C++ definition using template as C++ doesn't support compound litteral. +*/ +#define M_CSTR(...) \ + (const m_lib::m_char_array(__VA_ARGS__)).m_get() + +#else + +// Encapsulate snprintf to return the input buffer as argument (needed for M_CSTR) +static inline const char * +m_core_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + va_start (args, format); + int ret = vsnprintf(str, size, format, args); + (void) ret; + va_end (args); + return str; +} + +/* Return a constant string constructed based on the printf-liked formated string + and its arguments. + The string is constructed at run time and uses a temporary space on the stack. + If the constructed string is longer than M_USE_CSTR_ALLOC (default 256), + the string is truncated. + Example: + strlen( M_CSTR("Len=%d", 17) ) == 6 + NOTE: C definition using compound litteral. +*/ +#define M_CSTR(...) \ + m_core_snprintf( (char [M_USE_CSTR_ALLOC]){}, M_USE_CSTR_ALLOC, __VA_ARGS__) + +#endif + /************************************************************/ /********************* HASH selection ***********************/ diff --git a/tests/test-mcore.c b/tests/test-mcore.c index fcdc4e35..b1afc274 100644 --- a/tests/test-mcore.c +++ b/tests/test-mcore.c @@ -606,6 +606,16 @@ static void test_str_hash(void) assert (M_CALL_HASH(M_CSTR_OPLIST, str3) != 0); } +static void test_M_CSTR(void) +{ + assert ( strcmp(M_CSTR("Len=%d", 17), "Len=17") == 0); + assert ( strcmp(M_CSTR("Hello %s %c", "World", '!'), "Hello World !") == 0); + // Reduce allocation to test truncation +#undef M_USE_CSTR_ALLOC +#define M_USE_CSTR_ALLOC 8 + assert ( strcmp(M_CSTR("Hello %s %c", "World", '!'), "Hello W") == 0); +} + int main(void) { test_cat(); @@ -629,5 +639,6 @@ int main(void) test_move_default(); test_builtin(); test_str_hash(); + test_M_CSTR(); exit(0); }