[英]How to execute a single preprocessor directive in a C file
我有一个main.c
文件,其中包含一个或多个定义的预处理器宏:
#include <stdio.h>
#define VALUE 12
int main(void) {
printf("This file is in version %s and contains value %d\n", VERSION, VALUE);
return 0;
}
我想导出一个仅将#define VERSION "1.0"
应用于原始源文件的main2.c
文件。
我尝试了什么:
gcc -DVERSION=\"1.0\" -E
将应用所有预处理器指令而不是我想要的单个指令sed 's/VERSION/\"1.0\"/g'
可能会替换超过需要的内容,如果我需要多个指令,则需要更多的工作cppp
是一个不错的工具,但可能会大量更改源文件。 仅支持带数值的简单定义有没有办法只用 gcc 执行部分预处理器指令?
部分预处理是一个不错的想法,正是您正在寻找的。 Brian Raiter 的cppp实用程序仅处理#ifdef
和#ifndef
行,它不会根据您的需要执行宏替换。
这是我刚刚为此目的编写的一个实用程序:您可以在命令行上使用-Didentifier
(扩展为1
)或-Didentifier=
(扩展为空)、 -Didentifier=str
或简单的identifier=str
定义任意数量的标识符。
它将仅替换标识符,保留注释和字符串,但不处理一些极端情况,尽管这不应该是一个问题:
#include <stdio.h>
中的stdio
将被视为可以替换的标识符。1.0E+1
。include
、 ifdef
和其他预处理指令将导致它们被替换pcpp.c :
/* Partial preprocessing by chqrlie */
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct define_t {
struct define_t *next;
size_t len;
const char *tok;
const char *def;
} define_t;
static void *xmalloc(size_t size) {
void *p = malloc(size);
if (!p) {
fprintf(stderr, "pcpp: cannot allocate memory\n");
exit(1);
}
return p;
}
static void add_define(define_t **defsp, const char *str) {
define_t *dp = xmalloc(sizeof(*dp));
size_t len = strcspn(str, "=");
const char *def = str[len] ? str + len + 1 : "1";
dp->len = len;
dp->tok = str;
dp->def = def;
dp->next = *defsp;
*defsp = dp;
}
struct context {
FILE *fp;
int lineno;
size_t size, pos;
char *buf;
};
static int append_char(struct context *ctx, int ch) {
if (ctx->pos == ctx->size) {
size_t new_size = ctx->size + ctx->size / 2 + 32;
char *new_buf = xmalloc(new_size);
memcpy(new_buf, ctx->buf, ctx->size);
free(ctx->buf);
ctx->buf = new_buf;
ctx->size = new_size;
}
ctx->buf[ctx->pos++] = (char)ch;
return ch;
}
static void flush_context(struct context *ctx, FILE *ft) {
if (ctx->pos) {
fwrite(ctx->buf, ctx->pos, 1, ft);
ctx->pos = 0;
}
}
/* read the next byte from the C source file, handing escaped newlines */
static int getcpp(struct context *ctx) {
int ch;
while ((ch = getc(ctx->fp)) == '\\') {
append_char(ctx, ch);
if ((ch = getc(ctx->fp)) != '\n') {
ungetc(ch, ctx->fp);
return '\\';
}
append_char(ctx, ch);
ctx->lineno += 1;
}
if (ch != EOF)
append_char(ctx, ch);
if (ch == '\n')
ctx->lineno += 1;
return ch;
}
static void ungetcpp(struct context *ctx, int ch) {
if (ch != EOF && ctx->pos > 0) {
ungetc(ch, ctx->fp);
ctx->pos--;
}
}
static int preprocess(const char *filename, FILE *fp, const char *outname, define_t *defs) {
FILE *ft = stdout;
int ch;
struct context ctx[1] = {{ fp, 1, 0, 0, NULL }};
if (outname) {
if ((ft = fopen(outname, "w")) == NULL) {
fprintf(stderr, "pcpp: cannot open output file %s: %s\n",
outname, strerror(errno));
return 1;
}
}
while ((ch = getcpp(ctx)) != EOF) {
int startline = ctx->lineno;
if (ch == '/') {
if ((ch = getcpp(ctx)) == '/') {
/* single-line comment */
while ((ch = getcpp(ctx)) != EOF && ch != '\n')
continue;
if (ch == EOF) {
fprintf(stderr, "%s:%d: unterminated single line comment\n",
filename, startline);
//break;
}
//putc('\n', ft); /* replace comment with newline */
flush_context(ctx, ft);
continue;
}
if (ch == '*') {
/* multi-line comment */
int lastc = 0;
while ((ch = getcpp(ctx)) != EOF) {
if (ch == '/' && lastc == '*') {
break;
}
lastc = ch;
}
if (ch == EOF) {
fprintf(stderr, "%s:%d: unterminated comment\n",
filename, startline);
//break;
}
//putc(' ', ft); /* replace comment with single space */
flush_context(ctx, ft);
continue;
}
if (ch != '=') {
ungetcpp(ctx, ch);
}
flush_context(ctx, ft);
continue;
}
if (ch == '\'' || ch == '"') {
int sep = ch;
const char *const_type = (ch == '"') ? "string" : "character";
while ((ch = getcpp(ctx)) != EOF) {
if (ch == sep)
break;;
if (ch == '\\') {
if ((ch = getcpp(ctx)) == EOF)
break;
}
if (ch == '\n') {
fprintf(stderr, "%s:%d: unescaped newline in %s constant\n",
filename, ctx->lineno - 1, const_type);
/* This is a syntax error but keep going as if constant was terminated */
break;
}
}
if (ch == EOF) {
fprintf(stderr, "%s:%d: unterminated %s constant\n",
filename, startline, const_type);
}
flush_context(ctx, ft);
continue;
}
if (ch == '_' || isalpha(ch)) {
/* identifier or keyword */
define_t *dp;
while (isalnum(ch = getcpp(ctx)) || ch == '_')
continue;
ungetcpp(ctx, ch);
for (dp = defs; dp; dp = dp->next) {
if (dp->len == ctx->pos && !memcmp(dp->tok, ctx->buf, ctx->pos)) {
/* matching symbol */
fputs(dp->def, ft);
ctx->pos = 0;
break;
}
}
flush_context(ctx, ft);
continue;
}
if (ch == '.' || isdigit(ch)) {
/* preprocessing number: should parse precise syntax */
while (isalnum(ch = getcpp(ctx)) || ch == '.')
continue;
ungetcpp(ctx, ch);
flush_context(ctx, ft);
continue;
}
flush_context(ctx, ft);
}
if (outname) {
fclose(ft);
}
free(ctx->buf);
return 0;
}
int main(int argc, char *argv[]) {
char *filename = NULL;
char *outname = NULL;
define_t *defs = NULL;
FILE *fp;
int i;
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (*arg == '-') {
if (arg[1] == 'h' || arg[1] == '?' || !strcmp(arg, "--help")) {
printf("usage: pcpp [-o FILENAME] [-Dname[=value]] ... [FILE] ...\n");
return 2;
} else
if (arg[1] == 'o') {
if (arg[2]) {
outname = arg + 2;
} else
if (i + 1 < argc) {
outname = argv[++i];
} else {
fprintf(stderr, "pcpp: missing filename for -o\n");
return 1;
}
} else
if (arg[1] == 'D') {
if (arg[2]) {
add_define(&defs, arg + 2);
} else
if (i + 1 < argc) {
add_define(&defs, argv[++i]);
} else {
fprintf(stderr, "pcpp: missing definition for -D\n");
return 1;
}
} else {
fprintf(stderr, "pcpp: bad option: %s\n", arg);
return 1;
}
} else
if (strchr(arg, '=')) {
add_define(&defs, arg);
} else {
filename = arg;
if ((fp = fopen(filename, "r")) == NULL) {
fprintf(stderr, "pcpp: cannot open input file %s: %s\n",
filename, strerror(errno));
return 1;
}
preprocess(filename, fp, outname, defs);
fclose(fp);
}
}
if (!filename) {
preprocess("<stdin>", stdin, outname, defs);
}
return 0;
}
编辑:这是一个不可维护的解决方案 - 但它有效。 如果您希望您的项目随着时间的推移会发展成多个版本,请不要使用它。
我的尝试利用了预处理器条件代码和字符串连接(在 C 中您可以执行"abc" "def"
并且它将被视为"abcdef"
的事实。
#include <stdio.h>
#ifdef V1
#define VERSION "1"
#define VALUE 99
#else
#define VERSION "2"
#define VALUE 66
#endif
int main(void) {
printf("This file is in version " VERSION " and contains value %d\n", VALUE);
return 0;
}
哪个打印
>> ~/playground/so$ gcc -DV1 q1.c
>> ~/playground/so$ ./a.out
This file is in version 1 and contains value 99
>> ~/playground/so$ gcc -DV2 q1.c
>> ~/playground/so$ ./a.out
This file is in version 2 and contains value 66
阅读关于 autoconf https://www.gnu.org/software/autoconf/
甚至可能是关于汽车制造商(如果你想生成makefile) https://www.gnu.org/software/automake/ 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.