Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macro expands correctly, but gives me "expected expression" error

I've made a trivial reduction of my issue:

#define STR_BEG "
#define STR_END "

int main()
{
    char * s = STR_BEG abc STR_END;

    printf("%s\n", s);
}

When compiling this, I get the following error:

static2.c:12:16: error: expected expression
    char * s = STR_BEG abc STR_END;
               ^
static2.c:7:17: note: expanded from macro 'STR_BEG'
#define STR_BEG "

Now, if I just run the preprocessor, gcc -E myfile.c, I get:

int main()
{
    char * s = " abc ";


    printf("%s\n", s);
}

Which is exactly what I wanted, and perfectly legal resultant code. So what's the deal?

like image 794
Francisco Ryan Tolmasky I Avatar asked Jul 24 '14 00:07

Francisco Ryan Tolmasky I


1 Answers

The macro isn't really expanding "correctly", because this isn't a valid C Preprocessor program. As Kerrek says, the preprocessor doesn't quite work on arbitrary character sequences - it works on whole tokens. Tokens are punctuation characters, identifiers, numbers, strings, etc. of the same form (more or less) as the ones that form valid C code. Those defines do not describe valid strings - they open them, and fail to close them before the end of the line. So an invalid token sequence is being passed to the preprocessor. The fact it manages to produce output from an invalid program is arguably handy, but it doesn't make it correct and it almost certainly guarantees garbage output from the preprocessor at best. You need to terminate your strings for them to form whole tokens - right now they form garbage input.

To actually wrap a token, or token sequence, in quotes, use the stringification operator #:

#define STRFY(A) #A
STRFY(abc) // -> "abc"

GCC and similar compilers will warn you about mistakes like this if you compile or preprocess with the -Wall flag enabled.

(I assume you only get errors when you try to compile as C, but not when you do it in two passes, because internally to the compiler, it retains the information that these are "broken" tokens, which is lost if you write out an intermediate file and then compile the preprocessed source in a second pass... if so, this is an implementation detail, don't rely on it.)


One possible solution to your actual problem might look like this:

#define LPR (
#define start STRFY LPR
#define end )
#define STRFY(A) #A
#define ID(...) __VA_ARGS__

ID(
  char * s = start()()()end;  // -> char * s = "()()()";
)

The ID wrapper is necessary, though. There's no way to do it without that (it can go around any number of lines, or even your whole program, but it must exist for reasons that are well-covered in other questions).

like image 66
Leushenko Avatar answered Nov 13 '22 02:11

Leushenko