add cx_strcast_m()

Sun, 28 Dec 2025 14:47:36 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 28 Dec 2025 14:47:36 +0100
changeset 1673
0c338b80e7dd
parent 1672
94360453bce4
child 1674
8b0f162ac88e

add cx_strcast_m()

relates to #792

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/string.h.md file | annotate | diff | comparison | revisions
src/cx/string.h file | annotate | diff | comparison | revisions
tests/test_string.c file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Sun Dec 28 14:10:14 2025 +0100
+++ b/CHANGELOG	Sun Dec 28 14:47:36 2025 +0100
@@ -12,6 +12,7 @@
  * adds cx_hash_key_as_string()
  * adds support for CxHashKey pointers in CX_HASH_KEY() and all map functions
  * adds CX_NULLSTR convenience macro
+ * adds cx_strcast_m()
  * adds cx_strat()
  * adds cx_bstr() and cx_bstr_m()
  * adds cxBufferMaximumCapacity()
--- a/docs/Writerside/topics/about.md	Sun Dec 28 14:10:14 2025 +0100
+++ b/docs/Writerside/topics/about.md	Sun Dec 28 14:47:36 2025 +0100
@@ -39,6 +39,7 @@
 * adds cx_hash_key_as_string()
 * adds support for CxHashKey pointers in CX_HASH_KEY() and all map functions
 * adds CX_NULLSTR convenience macro
+* adds cx_strcast_m()
 * adds cx_strat()
 * adds cx_bstr() and cx_bstr_m()
 * adds cxBufferMaximumCapacity()
--- a/docs/Writerside/topics/string.h.md	Sun Dec 28 14:10:14 2025 +0100
+++ b/docs/Writerside/topics/string.h.md	Sun Dec 28 14:47:36 2025 +0100
@@ -14,6 +14,8 @@
 > To simplify documentation, we introduce the pseudo-type `AnyStr` with the meaning that
 > any UCX string and any C string are supported.
 > The implementation is actually hidden behind a macro which uses `cx_strcast()` to guarantee compatibility.
+> Similarly we introduce `UcxStr` with the meaning that it is either a `cxstring` or a `cxmutstr`,
+> created by `cx_strcast_m()`.
 {style="note"}
 
 ```C
@@ -49,10 +51,11 @@
 void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
 
 
-#define CX_NULLSTR   cx_mutstr(NULL)
-#define CX_SFMT(s)   (int) (s).length, (s).ptr
-#define CX_PRIstr    ".*s"
-#define cx_strcast(s)  // converts any string to cxstring
+#define CX_NULLSTR       cx_mutstr(NULL)
+#define CX_SFMT(s)       (int) (s).length, (s).ptr
+#define CX_PRIstr        ".*s"
+#define cx_strcast(s)    // converts any string to cxstring
+#define cx_strcast_m(s)  // converts any string to a UcxStr
 ```
 
 The functions `cx_str()` and `cx_mutstr()` create a UCX string from a `const char*` or a `char*`
@@ -75,6 +78,10 @@
 When you want to use a UCX string in a `printf`-like function, you can use the macro `CX_PRIstr` for the format specifier,
 and the `CX_SFMT(s)` macro to expand the arguments.
 
+The macros `cx_strcast()` and `cx_strcast_m()` take any supported string (`AnyStr`) and convert it either to a `cxstring` or a `cxmutstr`.
+While `cx_strcast()` _always_ converts to a `cxstring`, `cx_strcast_m()` decides depending on the constness of the string to return either a `cxstring` or a `cxmutstr`.
+You should rarely need those macros in your code, as most UCX functions are already implemented behind macros that do the conversions for you.
+
 > When you want to convert a string _literal_ into a UCX string, you can also use the `cx_str()` function.
 > With optimizations turned on, this function gets inlined and optimizes the call to `strlen()` out, giving
 > you a `cxstring` structure at zero cost with the length calculated at compile-time.
@@ -154,31 +161,19 @@
 ```C
 #include <cx/string.h>
 
-char cx_strat(cxstring str, off_t index);
-
-cxstring cx_strchr(cxstring string, int chr);
-
-cxstring cx_strrchr(cxstring string, int chr);
+char cx_strat(AnyStr str, off_t index);
 
-cxstring cx_strstr(cxstring string, AnyStr search);
+UcxStr cx_strchr(AnyStr string, int chr);
 
-cxstring cx_strsubs(cxstring string, size_t start);
-
-cxstring cx_strsubsl(cxstring string, size_t start, size_t length);
+UcxStr cx_strrchr(AnyStr string, int chr);
 
-cxstring cx_strtrim(cxstring string);
+UcxStr cx_strstr(AnyStr string, AnyStr search);
 
-cxmutstr cx_strchr_m(cxmutstr string, int chr);
-
-cxmutstr cx_strrchr_m(cxmutstr string, int chr);
+UcxStr cx_strsubs(AnyStr string, size_t start);
 
-cxmutstr cx_strstr_m(cxmutstr string, AnyStr search);
-
-cxmutstr cx_strsubs_m(cxmutstr string, size_t start);
+UcxStr cx_strsubsl(AnyStr string, size_t start, size_t length);
 
-cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length);
-
-cxmutstr cx_strtrim_m(cxmutstr string);
+UcxStr cx_strtrim(AnyStr string);
 ```
 
 The function `cx_strat()` returns the character at the specified `index`.
--- a/src/cx/string.h	Sun Dec 28 14:10:14 2025 +0100
+++ b/src/cx/string.h	Sun Dec 28 14:47:36 2025 +0100
@@ -243,41 +243,60 @@
 
 #ifdef __cplusplus
 cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(cxmutstr str) {
-    return cx_strn(str.ptr, str.length);
+CX_CPPDECL cxmutstr cx_strcast_m(cxmutstr str) {
+    return str;
 }
 cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(cxstring str) {
+CX_CPPDECL cxstring cx_strcast_m(cxstring str) {
     return str;
 }
 cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(const char *str) {
+CX_CPPDECL cxmutstr cx_strcast_m(char *str) {
+    return cx_mutstr(str);
+}
+cx_attr_nodiscard
+CX_CPPDECL cxmutstr cx_strcast_m(unsigned char *str) {
+    return cx_mutstr(reinterpret_cast<char*>(str));
+}
+cx_attr_nodiscard
+CX_CPPDECL cxstring cx_strcast_m(const char *str) {
     return cx_str(str);
 }
 cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(const unsigned char *str) {
+CX_CPPDECL cxstring cx_strcast_m(const unsigned char *str) {
     return cx_str(reinterpret_cast<const char*>(str));
 }
+cx_attr_nodiscard
+CX_CPPDECL cxstring cx_strcast_(cxmutstr str) {
+    return cx_strn(str.ptr, str.length);
+}
+cx_attr_nodiscard
+CX_CPPDECL cxstring cx_strcast_(cxstring str) {
+    return str;
+}
+#define cx_strcast(s) cx_strcast_(cx_strcast_m(s))
 extern "C" {
 #else
 /**
  * Internal function, do not use.
  * @param str
  * @return
+ * @see cx_strcast_m()
  * @see cx_strcast()
  */
 cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_m(cxmutstr str) {
-    return (cxstring) {str.ptr, str.length};
+CX_INLINE cxmutstr cx_strcast_cxms(cxmutstr str) {
+    return str;
 }
 /**
  * Internal function, do not use.
  * @param str
  * @return
+ * @see cx_strcast_m()
  * @see cx_strcast()
  */
 cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_c(cxstring str) {
+CX_INLINE cxstring cx_strcast_cxs(cxstring str) {
     return str;
 }
 
@@ -285,10 +304,35 @@
  * Internal function, do not use.
  * @param str
  * @return
+ * @see cx_strcast_m()
  * @see cx_strcast()
  */
 cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_u(const unsigned char *str) {
+CX_INLINE cxmutstr cx_strcast_uc(unsigned char *str) {
+    return cx_mutstr((char*)str);
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast_m()
+ * @see cx_strcast()
+ */
+cx_attr_nodiscard
+CX_INLINE cxmutstr cx_strcast_c(char *str) {
+    return cx_mutstr(str);
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast_m()
+ * @see cx_strcast()
+ */
+cx_attr_nodiscard
+CX_INLINE cxstring cx_strcast_ucc(const unsigned char *str) {
     return cx_str((const char*)str);
 }
 
@@ -296,10 +340,11 @@
  * Internal function, do not use.
  * @param str
  * @return
+ * @see cx_strcast_m()
  * @see cx_strcast()
  */
 cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_z(const char *str) {
+CX_INLINE cxstring cx_strcast_cc(const char *str) {
     return cx_str(str);
 }
 
@@ -307,15 +352,46 @@
  * Wraps any string into an UCX string.
  *
  * @param str (any supported string type) the string to cast
- * @return (@c cxstring) the string wrapped as UCX string
+ * @return (@c cxstring) or (@c cxmutstr) the string wrapped as UCX string
+ */
+#define cx_strcast_m(str) _Generic((str), \
+        cxmutstr: cx_strcast_cxms, \
+        cxstring: cx_strcast_cxs, \
+        const unsigned char*: cx_strcast_ucc, \
+        unsigned char *: cx_strcast_uc, \
+        const char*: cx_strcast_cc, \
+        char *: cx_strcast_c) (str)
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
  */
-#define cx_strcast(str) _Generic((str), \
-        cxmutstr: cx_strcast_m, \
-        cxstring: cx_strcast_c, \
-        const unsigned char*: cx_strcast_u, \
-        unsigned char *: cx_strcast_u, \
-        const char*: cx_strcast_z, \
-        char *: cx_strcast_z) (str)
+CX_INLINE cxstring cx_strcast_1(cxmutstr str) {
+    return (cxstring){str.ptr, str.length};
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ */
+CX_INLINE cxstring cx_strcast_2(cxstring str) {
+    return str;
+}
+
+/** internal conversion macro */
+#define cx_strcast_(str) _Generic((str), \
+        cxmutstr: cx_strcast_1, \
+        cxstring: cx_strcast_2)(str)
+
+/**
+ * Converts any string to a cxstring.
+ *
+ * @param str (any supported string type) the string to cast
+ * @return he string converted to a (@c cxstring)
+ */
+#define cx_strcast(str) cx_strcast_(cx_strcast_m(str))
 #endif
 
 /**
--- a/tests/test_string.c	Sun Dec 28 14:10:14 2025 +0100
+++ b/tests/test_string.c	Sun Dec 28 14:47:36 2025 +0100
@@ -63,15 +63,19 @@
     }
 }
 
-CX_TEST(test_string_cast) {
+CX_TEST(test_strcast) {
     char *c1 = (char*) "123";
     const char *c2 = "abcde";
     unsigned char *c3 = (unsigned char*) "4711";
     unsigned const char *c4 = (unsigned const char*) "xyz0815";
+    cxstring c5 = cx_str("foobar");
+    cxmutstr c6 = cx_mutstr((char*)"hello world");
     cxstring s1 = cx_strcast(c1);
     cxstring s2 = cx_strcast(c2);
     cxstring s3 = cx_strcast(c3);
     cxstring s4 = cx_strcast(c4);
+    cxstring s5 = cx_strcast(c5);
+    cxstring s6 = cx_strcast(c6);
     CX_TEST_DO {
         CX_TEST_ASSERT(s1.length == 3);
         CX_TEST_ASSERT(strncmp(s1.ptr, "123", 3) == 0);
@@ -81,6 +85,39 @@
         CX_TEST_ASSERT(strncmp(s3.ptr, "4711", 4) == 0);
         CX_TEST_ASSERT(s4.length == 7);
         CX_TEST_ASSERT(strncmp(s4.ptr, "xyz0815", 7) == 0);
+        CX_TEST_ASSERT(s5.length == 6);
+        CX_TEST_ASSERT(strncmp(s5.ptr, "foobar", 6) == 0);
+        CX_TEST_ASSERT(s6.length == 11);
+        CX_TEST_ASSERT(strncmp(s6.ptr, "hello world", 11) == 0);
+    }
+}
+
+CX_TEST(test_strcast_m) {
+    char *c1 = (char*) "123";
+    const char *c2 = "abcde";
+    unsigned char *c3 = (unsigned char*) "4711";
+    unsigned const char *c4 = (unsigned const char*) "xyz0815";
+    cxstring c5 = cx_str("foobar");
+    cxmutstr c6 = cx_mutstr((char*)"hello world");
+    cxmutstr s1 = cx_strcast_m(c1);
+    cxstring s2 = cx_strcast_m(c2);
+    cxmutstr s3 = cx_strcast_m(c3);
+    cxstring s4 = cx_strcast_m(c4);
+    cxstring s5 = cx_strcast_m(c5);
+    cxmutstr s6 = cx_strcast_m(c6);
+    CX_TEST_DO {
+        CX_TEST_ASSERT(s1.length == 3);
+        CX_TEST_ASSERT(strncmp(s1.ptr, "123", 3) == 0);
+        CX_TEST_ASSERT(s2.length == 5);
+        CX_TEST_ASSERT(strncmp(s2.ptr, "abcde", 5) == 0);
+        CX_TEST_ASSERT(s3.length == 4);
+        CX_TEST_ASSERT(strncmp(s3.ptr, "4711", 4) == 0);
+        CX_TEST_ASSERT(s4.length == 7);
+        CX_TEST_ASSERT(strncmp(s4.ptr, "xyz0815", 7) == 0);
+        CX_TEST_ASSERT(s5.length == 6);
+        CX_TEST_ASSERT(strncmp(s5.ptr, "foobar", 6) == 0);
+        CX_TEST_ASSERT(s6.length == 11);
+        CX_TEST_ASSERT(strncmp(s6.ptr, "hello world", 11) == 0);
     }
 }
 
@@ -1513,7 +1550,7 @@
     cxstring str = cx_str("Hello, World!");
     CX_TEST_DO {
         char actual[64];
-        snprintf(actual, 64, "Test %"CX_PRIstr " Success.", CX_SFMT(str));
+        snprintf(actual, 64, "Test %" CX_PRIstr " Success.", CX_SFMT(str));
         CX_TEST_ASSERT(0 == strncmp("Test Hello, World! Success.", actual, 64));
     }
 }
@@ -1522,7 +1559,8 @@
     CxTestSuite *suite = cx_test_suite_new("string");
 
     cx_test_register(suite, test_string_construct);
-    cx_test_register(suite, test_string_cast);
+    cx_test_register(suite, test_strcast);
+    cx_test_register(suite, test_strcast_m);
     cx_test_register(suite, test_strfree);
     cx_test_register(suite, test_strdup);
     cx_test_register(suite, test_strdup_shortened);

mercurial