<p>neels has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/15957">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">utils.h: add OSMO_NAME_C_IMPL() macro<br><br>Provide a common implementation for foo_name_c() functions that base on<br>foo_name_buf() functions.<br><br>  char *foo_name_c(void *ctx, example_t arg)<br>  {<br>          OSMO_NAME_C_IMPL(ctx, 64, "ERROR", foo_name_buf, arg)<br>  }<br><br>Rationale: the most efficient way of composing strings that have optional parts<br>or require loops for composition is by writing to a ready char[], and this in<br>turn is easiest done by using OSMO_STRBUF_* API. Using such a basic name string<br>implementation which typically returns a length, I often want a more convenient<br>version that returns a char*, which can just be inlined in a "%s" string format<br>-- crucially: skipping string composition when inlined in a LOGP(). This common<br>implementation allows saving code dup, only the function signature is needed.<br><br>Why not include the function signature in the macro? The two sets of varargs<br>(1: signature args, 2: function call args) are hard to do. Also, having an<br>explicit signature is good for readability and code grepping / ctags.<br><br>Upcoming uses: in libosmocore in the mslookup (D-GSM) implementation<br>(osmo_mslookup_result_name_c()), and in osmo_msc's codec negotiation<br>implementation (sdp_audio_codecs_name_c(), sdp_msg_name_c(), ...).<br>I54b6c0810f181259da307078977d9ef3d90458c9 (libosmocore)<br>If3ce23cd5bab15e2ab4c52ef3e4c75979dffe931 (osmo-msc)<br><br>Change-Id: Ida5ba8d9640ea641aafef0236800f6d489d3d322<br>---<br>M include/osmocom/core/utils.h<br>M tests/utils/utils_test.c<br>M tests/utils/utils_test.ok<br>3 files changed, 157 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/57/15957/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h</span><br><span>index 601bb56..4eba987 100644</span><br><span>--- a/include/osmocom/core/utils.h</span><br><span>+++ b/include/osmocom/core/utils.h</span><br><span>@@ -3,6 +3,7 @@</span><br><span> #include <stdbool.h></span><br><span> #include <stdint.h></span><br><span> #include <stdio.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span> </span><br><span> #include <osmocom/core/backtrace.h></span><br><span> #include <osmocom/core/talloc.h></span><br><span>@@ -269,4 +270,61 @@</span><br><span> </span><br><span> bool osmo_str_startswith(const char *str, const char *startswith_str);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Translate a buffer function to a talloc context function.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This is the full function body of a char *foo_name_c(void *ctx, val...) function, implemented by an</span><br><span style="color: hsl(120, 100%, 40%);">+ * int foo_name_buf(buf, buflen, val...) function:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *    char *foo_name_c(void *ctx, example_t arg)</span><br><span style="color: hsl(120, 100%, 40%);">+ *    {</span><br><span style="color: hsl(120, 100%, 40%);">+ *            OSMO_NAME_C_IMPL(ctx, 64, "ERROR", foo_name_buf, arg)</span><br><span style="color: hsl(120, 100%, 40%);">+ *    }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Return a talloc'd string containing the result of the given foo_name_buf() function, or ON_ERROR on error in the called</span><br><span style="color: hsl(120, 100%, 40%);">+ * foo_name_buf() function.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * If ON_ERROR is NULL, the function returns NULL on error rc from FUNC_BUF. Take care: returning NULL in printf() like</span><br><span style="color: hsl(120, 100%, 40%);">+ * formats (LOGP()) makes the program crash. If ON_ERROR is non-NULL, it must be a string constant, which is not</span><br><span style="color: hsl(120, 100%, 40%);">+ * returned directly, but written to an allocated string buffer first.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] INITIAL_BUFSIZE  Which size to first talloc from ctx -- a larger size makes a reallocation less likely, a</span><br><span style="color: hsl(120, 100%, 40%);">+ *        smaller size allocates less unused bytes, zero allocates once but still runs the string composition twice.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] ON_ERROR  String constant to copy on error rc returned by FUNC_BUF, or NULL to return NULL.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] FUNC_BUF  Name of a function with signature foo_buf(char *buf, size_t buflen, ...).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] FUNC_BUF_ARGS  Additional arguments to pass to FUNC_BUF after the buf and buflen.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define OSMO_NAME_C_IMPL(CTX, INITIAL_BUFSIZE, ON_ERROR, FUNC_BUF, FUNC_BUF_ARGS...) \</span><br><span style="color: hsl(120, 100%, 40%);">+        size_t len = INITIAL_BUFSIZE; \</span><br><span style="color: hsl(120, 100%, 40%);">+       int needed; \</span><br><span style="color: hsl(120, 100%, 40%);">+ char *str = NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+   if (INITIAL_BUFSIZE > 0) { \</span><br><span style="color: hsl(120, 100%, 40%);">+               str = (char*)talloc_named_const(CTX, len, __func__); \</span><br><span style="color: hsl(120, 100%, 40%);">+                OSMO_ASSERT(str); \</span><br><span style="color: hsl(120, 100%, 40%);">+   } \</span><br><span style="color: hsl(120, 100%, 40%);">+   needed = FUNC_BUF(str, len, ## FUNC_BUF_ARGS); \</span><br><span style="color: hsl(120, 100%, 40%);">+      if (needed < 0) \</span><br><span style="color: hsl(120, 100%, 40%);">+          goto OSMO_NAME_C_on_error; \</span><br><span style="color: hsl(120, 100%, 40%);">+  if (needed < len) \</span><br><span style="color: hsl(120, 100%, 40%);">+                return str; \</span><br><span style="color: hsl(120, 100%, 40%);">+ len = needed + 1; \</span><br><span style="color: hsl(120, 100%, 40%);">+   if (str) { \</span><br><span style="color: hsl(120, 100%, 40%);">+          str = (char*)talloc_realloc_size(CTX, str, len); \</span><br><span style="color: hsl(120, 100%, 40%);">+            talloc_set_name_const(str, __func__); \</span><br><span style="color: hsl(120, 100%, 40%);">+       } else \</span><br><span style="color: hsl(120, 100%, 40%);">+              str = (char*)talloc_named_const(CTX, len, __func__); \</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(str); \</span><br><span style="color: hsl(120, 100%, 40%);">+   needed = FUNC_BUF(str, len, ## FUNC_BUF_ARGS); \</span><br><span style="color: hsl(120, 100%, 40%);">+      if (needed < 0) \</span><br><span style="color: hsl(120, 100%, 40%);">+          goto OSMO_NAME_C_on_error; \</span><br><span style="color: hsl(120, 100%, 40%);">+  return str; \</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_NAME_C_on_error: \</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Re-using and re-sizing above allocated buf ends up in very complex code. Just free and strdup. */ \</span><br><span style="color: hsl(120, 100%, 40%);">+        if (str) \</span><br><span style="color: hsl(120, 100%, 40%);">+            talloc_free(str); \</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ON_ERROR) \</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+        str = talloc_strdup(CTX, ON_ERROR); \</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(str); \</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_set_name_const(str, __func__); \</span><br><span style="color: hsl(120, 100%, 40%);">+       return str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} */</span><br><span>diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c</span><br><span>index 70d017f..532bdbf 100644</span><br><span>--- a/tests/utils/utils_test.c</span><br><span>+++ b/tests/utils/utils_test.c</span><br><span>@@ -1058,6 +1058,87 @@</span><br><span>     startswith_test_str("abc", "xyz", false);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int foo_name_buf(char *buf, size_t buflen, const char *arg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!arg)</span><br><span style="color: hsl(120, 100%, 40%);">+             return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       return snprintf(buf, buflen, "%s", arg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *foo_name_c(void *ctx, const char *arg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_NAME_C_IMPL(ctx, 10, "ERROR", foo_name_buf, arg)</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *foo_name_c_null(void *ctx, const char *arg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_NAME_C_IMPL(ctx, 10, NULL, foo_name_buf, arg)</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *foo_name_c_zero(void *ctx, const char *arg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_NAME_C_IMPL(ctx, 0, "ERROR", foo_name_buf, arg)</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *foo_name_c_zero_null(void *ctx, const char *arg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_NAME_C_IMPL(ctx, 0, NULL, foo_name_buf, arg)</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void name_c_impl_test()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      char *test_strs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+         "test",</span><br><span style="color: hsl(120, 100%, 40%);">+             "longer than 10 chars",</span><br><span style="color: hsl(120, 100%, 40%);">+             NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    struct {</span><br><span style="color: hsl(120, 100%, 40%);">+              const char *label;</span><br><span style="color: hsl(120, 100%, 40%);">+            char *(*func)(void *, const char*);</span><br><span style="color: hsl(120, 100%, 40%);">+   } funcs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+         {</span><br><span style="color: hsl(120, 100%, 40%);">+                     "OSMO_NAME_C_IMPL(10, \"ERROR\")",</span><br><span style="color: hsl(120, 100%, 40%);">+                        foo_name_c,</span><br><span style="color: hsl(120, 100%, 40%);">+           },</span><br><span style="color: hsl(120, 100%, 40%);">+            {</span><br><span style="color: hsl(120, 100%, 40%);">+                     "OSMO_NAME_C_IMPL(10, NULL)",</span><br><span style="color: hsl(120, 100%, 40%);">+                       foo_name_c_null,</span><br><span style="color: hsl(120, 100%, 40%);">+              },</span><br><span style="color: hsl(120, 100%, 40%);">+            {</span><br><span style="color: hsl(120, 100%, 40%);">+                     "OSMO_NAME_C_IMPL(0, \"ERROR\")",</span><br><span style="color: hsl(120, 100%, 40%);">+                 foo_name_c_zero,</span><br><span style="color: hsl(120, 100%, 40%);">+              },</span><br><span style="color: hsl(120, 100%, 40%);">+            {</span><br><span style="color: hsl(120, 100%, 40%);">+                     "OSMO_NAME_C_IMPL(0, NULL)",</span><br><span style="color: hsl(120, 100%, 40%);">+                        foo_name_c_zero_null,</span><br><span style="color: hsl(120, 100%, 40%);">+         },</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        void *ctx = talloc_named_const(NULL, 0, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+    int allocs = talloc_total_blocks(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      printf("\n%s\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < ARRAY_SIZE(test_strs); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+              char *test_str = test_strs[i];</span><br><span style="color: hsl(120, 100%, 40%);">+                int j;</span><br><span style="color: hsl(120, 100%, 40%);">+                printf("%2d: %s\n", i, osmo_quote_str(test_str, -1));</span><br><span style="color: hsl(120, 100%, 40%);">+               </span><br><span style="color: hsl(120, 100%, 40%);">+              for (j = 0; j < ARRAY_SIZE(funcs); j++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  char *str = funcs[j].func(ctx, test_str);</span><br><span style="color: hsl(120, 100%, 40%);">+                     printf("  %30s -> %s", funcs[j].label, osmo_quote_str(str, -1));</span><br><span style="color: hsl(120, 100%, 40%);">+                 printf("  allocated %d", (int)talloc_total_blocks(ctx) - allocs);</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (str) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            printf("  %zu bytes, name '%s'", talloc_total_size(str), talloc_get_name(str));</span><br><span style="color: hsl(120, 100%, 40%);">+                             talloc_free(str);</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     printf("\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     talloc_free(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int main(int argc, char **argv)</span><br><span> {</span><br><span>       static const struct log_info log_info = {};</span><br><span>@@ -1078,5 +1159,6 @@</span><br><span>  strbuf_test();</span><br><span>       strbuf_test_nolen();</span><br><span>         startswith_test();</span><br><span style="color: hsl(120, 100%, 40%);">+    name_c_impl_test();</span><br><span>  return 0;</span><br><span> }</span><br><span>diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok</span><br><span>index c150a8d..b603647 100644</span><br><span>--- a/tests/utils/utils_test.ok</span><br><span>+++ b/tests/utils/utils_test.ok</span><br><span>@@ -360,3 +360,20 @@</span><br><span> osmo_str_startswith("abc", "abc") == true</span><br><span> osmo_str_startswith("abc", "abcd") == false</span><br><span> osmo_str_startswith("abc", "xyz") == false</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+name_c_impl_test</span><br><span style="color: hsl(120, 100%, 40%);">+ 0: "test"</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_NAME_C_IMPL(10, "ERROR") -> "test"  allocated 1  10 bytes, name 'foo_name_c'</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_NAME_C_IMPL(10, NULL) -> "test"  allocated 1  10 bytes, name 'foo_name_c_null'</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_NAME_C_IMPL(0, "ERROR") -> "test"  allocated 1  5 bytes, name 'foo_name_c_zero'</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_NAME_C_IMPL(0, NULL) -> "test"  allocated 1  5 bytes, name 'foo_name_c_zero_null'</span><br><span style="color: hsl(120, 100%, 40%);">+ 1: "longer than 10 chars"</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_NAME_C_IMPL(10, "ERROR") -> "longer than 10 chars"  allocated 1  21 bytes, name 'foo_name_c'</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_NAME_C_IMPL(10, NULL) -> "longer than 10 chars"  allocated 1  21 bytes, name 'foo_name_c_null'</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_NAME_C_IMPL(0, "ERROR") -> "longer than 10 chars"  allocated 1  21 bytes, name 'foo_name_c_zero'</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_NAME_C_IMPL(0, NULL) -> "longer than 10 chars"  allocated 1  21 bytes, name 'foo_name_c_zero_null'</span><br><span style="color: hsl(120, 100%, 40%);">+ 2: NULL</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_NAME_C_IMPL(10, "ERROR") -> "ERROR"  allocated 1  6 bytes, name 'foo_name_c'</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_NAME_C_IMPL(10, NULL) -> NULL  allocated 0</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_NAME_C_IMPL(0, "ERROR") -> "ERROR"  allocated 1  6 bytes, name 'foo_name_c_zero'</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_NAME_C_IMPL(0, NULL) -> NULL  allocated 0</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/15957">change 15957</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/15957"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ida5ba8d9640ea641aafef0236800f6d489d3d322 </div>
<div style="display:none"> Gerrit-Change-Number: 15957 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>