Quote a macro preprocessor using -D - c

We can use the output of a command (with flags) as macro pre-procesor (eg. uname --kernel-name)
#include <stdio.h>
#define version(v) #v
#define VERSION(v) version(v)
int main(void)
{
printf("Version: %s\n", VERSION(kernel_name));
return 0;
}
Compiled using:
gcc -Wall -pedantic -o demo demo.c -Dkernel_name=$(uname --kernel-name)
Output:
Version: Linux
But this fail when macro begins with # (eg. uname --kernel-version):
david#debian:~$ uname --kernel-version
#1 SMP Debian 3.2.46-1+deb7u1
Because you can not define a preprocessor starting with #
#define SOMETHING #something /* error: stray ‘#’ in program */
Is there way to quote the output of command?
"#1 SMP Debian 3.2.46-1+deb7u1"
in order to get a valid input?

Easy.
Replace
-Dkernel_name=$(uname --kernel-name)
with
-Dkernel_name="\"$(uname --kernel-name)\""
This works because the -D flag is equivalent to a define. So -Dvar=val is equivalent to having a #define var val in your source.
When you do -Dkernel_name="\"$(uname --kernel-name)\"", your shell converts the define option value to equal "#1 SMP Debian 3.2.46-1+deb7u1" - one set of quotes for your shell and another for your actual program. This saves you from having to stringify too.

Related

Define multiple-word macro using -D flag with gcc

The purpose of this is to build a program with command line-injected macros, using a Makefile.
I would like to define macros using multiple terms, however I am given an error as subsequent parts of the string are treated as files by gcc.
An example of what I need is as follows:
#define ULL unsigned long long
#define T_ULL typedef unsigned long long ull_t
As a result, I am only able to create macros that contain 1 term per definition.
The latter attempt allows me to create parameterized macros, however those are also limited to 1 term per definition.
Attempted solution
#include <stdio.h>
#define _STRINGIZE(x) #x
#define STRINGIZE(x) _STRINGIZE(x)
int main(void)
{
# ifdef DEBUG
# ifdef STRING
printf("%s", "A STRING macro was defined.\n");
printf("string: %s\n", STRINGIZE(STRING));
# else
printf("%s\n", "A DEBUG macro was defined.");
# endif
# endif
}
Results
As described by the man page, under the -D option description.
$ gcc define.c -D='DEBUG' ; ./a.out
A DEBUG macro was defined.
As described by this answer, as an alternative approach.
$ gcc define.c -D'DEBUG' ; ./a.out
A DEBUG macro was defined.
$ gcc define.c -D'DEBUG' -D'STRING="abc"' ; ./a.out
A STRING macro was defined.
string: "abc"
$ gcc define.c -D'DEBUG' -D'STRING="abc efg"' ; ./a.out
clang: error: no such file or directory: 'efg"'
A STRING macro was defined.
string: "abc"
$ gcc define.c -D'DEBUG' -D'STRING="abc efg hij"' ; ./a.out
clang: error: no such file or directory: 'efg'
clang: error: no such file or directory: 'hij"'
A DEBUG macro was defined.
string: "abc"
You don't need the STRINGIZE macro. The correct command-line syntax is:
gcc -DDEBUG -DSTRING='"abc def"' program.c
In other words, you need to quote the whole value of the defined macro, including C string delimiters (").
Then you can just do:
printf("string: %s\n", STRING);

How to partially preprocess a C file with specific working directory

I would like to expand include directives of a C file of my working directory only; not the system directory.
I tried the following:
gcc -E -nostdinc -I./ input.c
But it stops preprocessing when it fails to find the included system headers in input.c. I would like it to copy the include directive when it can't find it and keep preprocessing the file.
if your input.c file contains some system headers, it's normal that the preprocessor crashes when it cannot find them.
You could first use grep -v to remove all #include of system headers in your code, achieving something like this (list is non-exhaustive):
grep -vE "(stdio|stdlib)\.h" code.c > code_.c
you get for instance:
#define EXITCODE 0
int main(){
int i = EOF;
printf("hello\n");
return EXITCODE;
}
then pre-process:
S:\c>gcc -E code_.c
# 1 "code_.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "code_.c"
int main(){
int i = EOF;
printf("hello\n");
return 0;
}
note that the pre-processor doesn't care about functions or macros not defined. You get your code preprocessed (and your macros expanded), not the system ones.
You have to process all included files as well of course. That means an extra layer of tools to create temp source files and work from there.
I found a utility that does exactly what I was looking for:
$ cpphs --nowarn --nomacro -I./ input.c | sed -E 's|#line 1 "missing file: (.*)"|#include <\1>|'

Expanding a dynamic macro inside printf function

I want to compile a c program under different names. In the main file, I have the following macro:
#ifndef EXECUTABLE_NAME
#define EXECUTABLE_NAME "defaultname"
#endif
When compiling using gcc, I use -D switch to define this macro (is that the right term?):
$ gcc main.c -DEXECUTABLE_NAME="newname"
I do not know if the -D works fine or not because when I try to do something like the code below...it fails...I can not tell if the -D switch is screwing up or the syntax of using the macro inside the printf() function is not correct:
printf("no or invalid arguments. usage: %s [0,255]\n", EXECUTABLE_NAME);
I get this error:
main.c:48:57: note: in expansion of macro ‘EXECUTABLE_NAME’
printf("no or invalid arguments. usage: %s [0,255]\n", EXECUTABLE_NAME);
Any suggestions?
It works, and you can see that because of the error. Which by the way is because you're defining your macro wrong on the command line, this is how you quote a string in a macro:
$ gcc main.c -DEXECUTABLE_NAME='"newname"'
And this said, it's not even needed. The executable path is always your first argument: *argv.
I tried this program with an integer, which is as follows
#ifndef EXECUTABLE_NAME
#define EXECUTABLE_NAME 2
#endif
int main()
{
printf("%d",EXECUTABLE_NAME);
}
and then typed gcc main.c -DEXECUTABLE_NAME=1 and it works as the value changes to 1. Is it a problem with the data type 'string' that you are using?

Avoiding a double macro subsitution in the C pre-processor

Here's a simple little C program that had me confused for a while:
#include <stdio.h>
#define STR1(x) #x
#define STR(x) STR1(x)
int main(void) {
printf("%s\n", STR(MYDEF));
}
This just prints the value of the MYDEF #define as a string, using the standard stringizing double-define technique.
Compile (on Linux) with gcc -DMYDEF=abc prog.c run the result and, not surprisingly, it prints out 'abc'.
But change the value gcc -DMYDEF=linux prog.c and the result printed is not 'linux' but '1'.
So that confused me for a bit, but of course it happens because gcc (on Linux) has, I discovered, a built-in #define for the name 'linux' with a value '1', and the STR(x) macro ends up expanding MYDEF to 'linux' then linux to '1'.
In my real program (which was rather more complex than the little test above) I got round this by doing things in a different (probably better) way, but it left me curious ... is there a simple little macro technique that would avoid this double-substitution and make the program print out 'linux'? I know I could add a -U or #undef of linux, but that feels a bit clumsy.
I had thought all the built-in #defines start with underscores (usually double underscores), but I guess not.
There is no way to expand a macro only once, there's always a rescan performing further replacement (never recursive, of course). There are circumstances where macros aren't expanded at all (as with the # operator), which is why you need the extra replacement level with two #define like in your example.
In ISO C, identifiers without a leading underscore are free for you to use (not all of them, to be precise). The GNU C dialects define some other macros by default (like linux) for backwards compatibility, though they plan to remove such macros in the future.
To get a list of such macros on your machine, you can do:
$ echo | gcc -std=gnu99 -E -dM - | grep -v '# *define *_'
#define unix 1
#define linux 1
#define i386 1
With the options for ISO C (-ansi/-std=c89, -std=c99, -std=c11/-std=c1x for older Gcc), these macros are not defined:
$ cat test.c
#define STR1(x) #x
#define STR(x) STR1(x)
STR(MYDEF);
STR1(MYDEF);
$ gcc -std=gnu99 -DMYDEF=linux -E test.c
# 1 "test.c"
# 1 "<command-line>"
# 1 "test.c"
"1";
"MYDEF";
$ gcc -std=c99 -DMYDEF=linux -E test.c
# 1 "test.c"
# 1 "<command-line>"
# 1 "test.c"
"linux";
"MYDEF";
In ISO C mode, these macros properly are in the reserved namespace:
$ echo | gcc -std=c99 -E -dM - | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
I see in the gcc manual that you can use the -ansi option to turn off predefined macros like "linux"
gcc -ansi -DMYDEF=linux prog.c

gcc macro define option doesn't work for string

Sample code:
main()
{
printf("%d\n", MARCO);
// printf("%s\n", MARCO);
}
When I try to use gcc -D option, I found the following command works:
gcc -D MARCO=12345 test.c
but when I change 12345 to a string:
gcc -D MARCO=abcde test.c
an error occurs:
error: ‘abcde’ undeclared (first use in this function)
I have tried -DMARCO=abcde, -DMARCO="abcde", -D MARCO="abcde"; all failed with that error.
Does this -D option only support integers?
The trouble is that double quotes are recognized by the shell and removed, unless you prevent the shell from doing so by escaping the double quotes (with backslashes) or enclosing them in single quotes (which is what I'd use):
gcc -DMARCO='"abcde"' test.c
The single quotes are stripped by the shell but that means that the double quotes are seen by the C preprocessor. You need to use the %s format, of course.
By changing the macro, you can stringify a non-quoted name on the command line:
#include <stdio.h>
#define STRINGIFY(x) #x
#define MACRO(x) STRINGIFY(x)
int main(void)
{
printf("%s\n", MACRO(MARCO));
return(0);
}
Compile that with gcc -o testprog -DMARCO=abcde test.c and you will find it produces the correct answer.
you can also use like this...
-DMACRO="\"abcde\""
Ref:
How do I pass a quoted string with -D to gcc in cmd.exe?
The macro MARCO is literally replaced by the string you entered and only then is the code compiled. Since there are no quotes around the string (the double quotes in two of the examples are interpreted as delimiters by the shell), the abcde is not interpreted as a string, but as an identifier. Since it isn't defined, the code fails to compile.

Resources