繁体   English   中英

如何在 C 文件中执行单个预处理器指令

[英]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定义任意数量的标识符。

它将仅替换标识符,保留注释和字符串,但不处理一些极端情况,尽管这不应该是一个问题:

  • 不支持非 ASCII 标识符。
  • #include <stdio.h>中的stdio将被视为可以替换的标识符。
  • 一些数字将被解析为 3 个标记: 1.0E+1
  • 如果使用转义换行符将标识符拆分为多行,则不会替换标识符
  • 与 C 预处理器不同,定义includeifdef和其他预处理指令将导致它们被替换
  • 宏参数名称可以被替换,而 C 预处理器会保留它们。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM