为什么我的 C/C++ header 解析器不工作?

Why does my C/C++ header parser not work?

我想将 Boost Preprocessor 库(最终还有其他库)捆绑到一个合并的 header 中,所以我拼凑了一个小的实用程序来实现这个目标......只是它不起作用!我无法确定是我的程序中的错误还是实现问题(或两者)导致它无法正常工作。

该程序应该打开 boost\preprocessor\library.hpp header(包括整个库)并递归输出到 stdout 库需要的所有 header。 Windows Explorer 报告目录树​​中有(自 Boost Preprocessor v1.59.0 起)270 个 header 个文件,但我的程序仅解析 204 个。

我通过在另一个使用 Boost 预处理器的项目中使用它来测试合并的 header。当使用 boost\preprocessor\library.hpp header 时,项目编译并工作正常,但是当使用我的合并版本时,编译失败,因为没有找到所有需要的升压预处理器宏。

完整的可编译代码:(仅使用 MSVC v19 测试)

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <unordered_map>

// Remember what header files have already been parsed.
std::unordered_map<std::string, bool> have_parsed;

char include_dir[FILENAME_MAX]; // Passed in from the command line.

// Advance given pointer to next non-whitespace character and return it.
char* find_next_nonwhitespace_char(char* start) {
    while(isspace((int)(*start)) != 0) start++;
    return start;
}

#define DIE(condition, str) if(condition) {perror(str); exit(EXIT_FAILURE);}

int headers_parsed = 0;

void parse_header(const char* filename) {
    headers_parsed++;
    char path[FILENAME_MAX];
    strcpy(path, include_dir);
    strcat(path, filename);

    // Open file, get size and slurp it up.
    FILE* file = fopen(path, "rb");
    DIE(file == NULL, "fopen()");
    fseek(file, 0L, SEEK_END);
    long int file_size = ftell(file);
    rewind(file);
    char* file_buffer = (char*)malloc(file_size+1); // +1 for extra '[=12=]'
    DIE(file_buffer == NULL, "malloc()");
    size_t got = fread(file_buffer, 1, file_size, file);
    DIE(got != file_size, "fread()");
    fclose(file);

    char* read_index = file_buffer;
    char* end_of_file = file_buffer + file_size;
    *end_of_file = '[=12=]';

    // File is now in memory, parse each line.
    while(read_index < end_of_file) {
        char* start_of_line = read_index;
        // Scan forward looking for newline or 'EOF'
        char* end_of_line = strchr(start_of_line, '\n');
        if(end_of_line == NULL) end_of_line = end_of_file;
        *end_of_line = '[=12=]';
        // Advance to the start of the next line for the next read.
        read_index += (end_of_line - start_of_line) + 1;
        // Look for #include directive at the start of the line.
        char* first_char = find_next_nonwhitespace_char(start_of_line);
        if(*first_char == '#') {
            // This could be an include line...
            char* next_char = find_next_nonwhitespace_char(first_char + 1);
            const char include[] = "include ";
            if(strncmp(next_char, include, strlen(include)) == 0) {
                char* open_brace = find_next_nonwhitespace_char(next_char + strlen(include));
                if(*open_brace++ == '<') {
                    char* close_brace = strchr(open_brace, '>');
                    assert(close_brace != NULL);
                    *close_brace = '[=12=]';
                    if(have_parsed[open_brace] == false) {
                        have_parsed[open_brace] = true;
                        parse_header(open_brace); // Recurse
                    }
                    continue;
                }
            }
        }
        fprintf(stdout, "%s\n", start_of_line);
    }
    free(file_buffer);
}

int main(int argc, char* argv[]) {
    if(argc < 3) {
        fprintf(stderr, "%s {include directory} {first header}\n", argv[0]);
        return EXIT_FAILURE;
    }

    // Ensure the include directory has trailing slash
    strcpy(include_dir, argv[1]);
    size_t len = strlen(argv[1]);
    if(include_dir[len-1] != '\') {
        include_dir[len] = '\';
        include_dir[len+1] = '[=12=]';
    }

    parse_header(argv[2]);

    fprintf(stderr, "headers parsed: %d\n", headers_parsed);
    return EXIT_SUCCESS;
}

运行编译后的程序:(Boost安装在g:\dev)

g:\dev\amalgamate\amalgamate.exe g:\dev\ boost\preprocessor\library.hpp > boost_pp.h
headers parsed: 204

并生成boost_pp.hheader:https://copy.com/vL6xdtScLogqnv9z

怎么了? 为什么我的程序没有创建一个有效的合并 header?

树中的某些 headers 实际上并未包含在 library.hpp 中,原因是:

  • 它们将其他 headers 包装为外部接口(或者因为它们已被弃用),例如 preprocessor/comma.hpp 仅包含 preprocessor/punctuation/comma.hpp

  • 要通过宏包含,例如:

    # define BOOST_PP_ITERATE() BOOST_PP_CAT(BOOST_PP_ITERATE_, BOOST_PP_INC(BOOST_PP_ITERATION_DEPTH()))
    #
    # define BOOST_PP_ITERATE_1 <boost/preprocessor/iteration/detail/iter/forward1.hpp>
    # define BOOST_PP_ITERATE_2 <boost/preprocessor/iteration/detail/iter/forward2.hpp>
    # define BOOST_PP_ITERATE_3 <boost/preprocessor/iteration/detail/iter/forward3.hpp>
    # define BOOST_PP_ITERATE_4 <boost/preprocessor/iteration/detail/iter/forward4.hpp>
    # define BOOST_PP_ITERATE_5 <boost/preprocessor/iteration/detail/iter/forward5.hpp>
    

可用于:

#define BOOST_PP_ITERATION_PARAMS_1 (...)
#include BOOST_PP_ITERATE()