This usually fails to compile when the caller fails to provide a string literal argument. As with SVLEN_SAFE beforeIn fact, this version willwhen I used GCC 14.2 to compile SV_SAFE("this" - "that"), but unlike SVLEN_SAFEtest cases with this macro, all cases that attempted to call SV_SAFE will (probably) generatewith anything other than a warning herestring literal argument failed to compile. One test case, (or an error ifSV_SAFE("abc" - "def") failed to compile in this test, but some compilers might compile it with a warning; use -Werror or similar is enabled) since this would attempt to assign an integer value (the value of "this" - "that") to a pointer variable (the s field of a str_view struct)force compiler errors here.
Test Program
Here is a test program to verify that SV_SAFE works and that it fails to compile for unwanted inputs. Failing tests are commented out. It may be possible to break this macro, but it appears to guard against non-string-literal inputs reasonably well. Failing tests are commented out.
I have also included an implementation of make_sv which I alluded to earlier in this answer. make_sv is a function which takes a pointer argument instead of a string literal. This has several advantages, one of which is much better error messages. Since it takes a pointer instead of a string literal you could use it with dynamically created strings or with arrays containing strings.
The compiler errors shown in comments to the right of test cases were generated by GCC 14.2 with the invocation gcc string_view_tests.c. This compiled in the default gnu17 mode, with no warnings enabled.
#include <stdio.h>
#include <string.h>
typedef struct {
const char *s;
size_t len;
} str_view;
#define SV_SAFE(str) ((str_view){(str ""), sizeof(str) - 1U})
// Do we really need an `SV` macro at all?
// `make_sv` returns an empty `str_view` if it receives a null pointer argument.
str_view make_sv(const char *s) {
if (s) {
return (str_view){ .s = s,
.len = strlen(s)};
}
return (str_view){ .s = "",
.len = 0};
}
int main(void) {
const char *s = "hello";
char word[] = "hello";
double a = 0.0f;
double *d = &a;
puts("testing SV_SAFE macro:");
str_view this = SV_SAFE("this macro");
printf("%s: .len = %zu\n", this.s, this.len);
// Some tests that should fail:
// str_view test = SV_SAFE(NULL); //--> error: expected ')' before string constant
// str_view test = SV_SAFE(word); //--> error: expected ')' before string constant
// str_view test = SV_SAFE(d); //--> error: expected ')' before string constant
// str_view test = SV_SAFE(s); //--> error: expected ')' before string constant
// str_view test = SV_SAFE(a); //--> error: expected ')' before string constant
// str_view test = SV_SAFE(x); //--> error: expected ')' before string constant
// str_view test = SV_SAFE(-); //--> error: wrong type argument to unary minus
// str_view test = SV_SAFE(); //--> error: expected expression before ')' token
// Some implementations might compile this test with a warning.
// Use `-Werror` or similar to force an error.
// str_view test = SV_SAFE("abc" - "def"); //--> error: initialization of 'const char *' from 'long long int' makes pointer from integer without a cast [-Wint-conversion]
puts("\ntesting make_sv function:");
str_view that = make_sv("that function");
printf("%s: .len = %zu\n", that.s, that.len);
puts("\ntesting make_sv(NULL):");
str_view null_test = make_sv(NULL);
printf("%s: .len = %zu\n", null_test.s, null_test.len);
puts("\ntesting make_sv(word):");
str_view word_test = make_sv(word);
printf("%s: .len = %zu\n", word_test.s, word_test.len);
puts("\ntesting make_sv(s):");
str_view s_test = make_sv(s);
printf("%s: .len = %zu\n", s_test.s, s_test.len);
// Some tests that should fail:
// str_view test = make_sv(NULL); // no longer failing
// str_view test = make_sv(word); // no longer failing
// str_view test = make_sv(d); //--> error: passing argument 1 of 'make_sv' from incompatible pointer type [-Wincompatible-pointer-types]
// str_view test = make_sv(s); // no longer failing
// str_view test = make_sv(a); //--> error: incompatible type for argument 1 of 'make_sv'
// str_view test = make_sv(x); //--> error: 'x' undeclared (first use in this function)
// str_view test = make_sv(-); //--> error: expected expression before ')' token
// str_view test = make_sv(); //--> error: too few arguments to function 'make_sv'
// str_view test = make_sv("abc" - "def"); //--> error: passing argument 1 of 'make_sv' makes pointer from integer without a cast [-Wint-conversion]
}
```