3
\$\begingroup\$

The requirement is to define a macro which takes a single argument that is a string literal.

My first try at it was to surround it with empty string literals (I got this from Modern C):

#define SV(str)    (sizeof("" str "") - 1)

Now if I do:

int main(void)
{
    static const char *const s = "hello";
    char word[] = "hello";
    double a = 0.0f;
    double *d = &a;

    printf("%zu\n", SV(NULL));
    printf("%zu\n", SV(word));
    printf("%zu\n", SV(d));
    printf("%zu\n", SV(s));

    return 0;
}

It fails to compile, which was the expected behavior:

macro_str.c: In function ‘main’:
macro_str.c:4:33: error: called object is not a function or function pointer
    4 | #define SV(str)         (sizeof("" str "") - 1)
      |                                 ^~
macro_str.c:19:21: note: in expansion of macro ‘SV’
   19 |     printf("%zu\n", SV(NULL));
      |                     ^~
macro_str.c:4:40: error: expected ‘)’ before string constant
    4 | #define SV(str)         (sizeof("" str "") - 1)
      |                                ~       ^~
macro_str.c:19:21: note: in expansion of macro ‘SV’
   19 |     printf("%zu\n", SV(NULL));
      |                     ^~
macro_str.c:20:24: error: expected ‘)’ before ‘word’
   20 |     printf("%zu\n", SV(word));
      |                        ^~~~
macro_str.c:4:36: note: in definition of macro ‘SV’
    4 | #define SV(str)         (sizeof("" str "") - 1)
      |                                    ^~~
macro_str.c:4:32: note: to match this ‘(’
    4 | #define SV(str)         (sizeof("" str "") - 1)
      |                                ^
macro_str.c:20:21: note: in expansion of macro ‘SV’
   20 |     printf("%zu\n", SV(word));
      |                     ^~
macro_str.c:21:24: error: expected ‘)’ before ‘d’
   21 |     printf("%zu\n", SV(d));
      |                        ^
macro_str.c:4:36: note: in definition of macro ‘SV’
    4 | #define SV(str)         (sizeof("" str "") - 1)
      |                                    ^~~
macro_str.c:4:32: note: to match this ‘(’
    4 | #define SV(str)         (sizeof("" str "") - 1)
      |                                ^
macro_str.c:21:21: note: in expansion of macro ‘SV’
   21 |     printf("%zu\n", SV(d));
      |                     ^~
macro_str.c:22:24: error: expected ‘)’ before ‘s’
   22 |     printf("%zu\n", SV(s));
      |                        ^
macro_str.c:4:36: note: in definition of macro ‘SV’
    4 | #define SV(str)         (sizeof("" str "") - 1)
      |                                    ^~~
macro_str.c:4:32: note: to match this ‘(’
    4 | #define SV(str)         (sizeof("" str "") - 1)
      |                                ^
macro_str.c:22:21: note: in expansion of macro ‘SV’
   22 |     printf("%zu\n", SV(s));
      |                     ^~
make: *** [<builtin>: macro_str] Error 1

Then I proceeded to ask a question on StackOverflow and found instances where this would fail:

SV()
SV(/*comment*/)
SV(-)
SV("abc" - "def")

So I added some more expressions , suggested by @n. m. could be an AI, to it:

#define SV(str)    ("" str "", (str)[0], sizeof(str) - 1)

Now it fails for these 4 edge-cases as well. Yet there are still things like:

// I believe the second one is defined-behavior. 
SV("abcd" - sizeof "e");  
SV("abcd" + sizeof "e"); 

for which it would compile cleanly. Whilst I am satisfied with the final macro and it serves my use-case, I believe there might still be some improvements it could use.

How can it be made more robust?

Note: I am looking for a solution that would work with at least C99.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ This was a simple use-case. A better use case is for a debug_print(s, x) wrapper around fprintf(stderr, s, x) where s is required to be a string literal for security purposes. \$\endgroup\$
    – Harith
    Mar 28 at 16:15
  • 1
    \$\begingroup\$ The use-case seems similar to Stringizing. You'll still end up with situations where you can't stringify empty arguments or comments. \$\endgroup\$
    – Rish
    Mar 29 at 2:39

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.