Как сделать clang -E пропустить предварительно скомпилированный заголовок

PrecompiledHeader.h:

#include "stdio.h"

main.cpp:

#include "PrecompiledHeader.h"
#include "stdio.h"

int main()
{
   return 123;
}

Создание предварительно скомпилированного заголовка:

clang -x c++-header PrecompiledHeader.h -o PrecompiledHeader.pch

Запуск препроцессора clang на main.cpp:

clang main.cpp -include-pch PrecompiledHeader.pch -E 

В настоящее время выводится много много страниц вывода (stdio.h). Однако то, что я хотел бы получить, - это только основная функция, и содержимое PrecompiledHeader.h будет опущено на выходе.

Есть ли способ заставить clang сделать это? (если кто-то знает ответ на тот же вопрос для visualstudio cl.exe, я хотел бы узнать об этом тоже:)

Ответ 1

Вряд ли такой способ существует или будет встроен в clang (или любой другой компилятор С++) в будущем. Проблема заключается в том, что прекомпилированные заголовки используются для ускорения этапа компиляции, который обычно значительно дороже, чем шаг предварительной обработки. В результате, если вы собираетесь только предварительно обработать ваш источник, предварительно скомпилированный заголовок просто не будет использоваться. Один из способов, который имеет пренебрежимо малую вероятность создать то, что вам нужно, - использовать параметр -include вместо параметра -include-pch (обратите внимание, что вы должны указать ему *.h, а не файл *.pch):

clang main.cpp -include PrecompiledHeader.h -E

Гарантированный способ решения вашей проблемы состоит в том, чтобы передать предварительно обработанный вывод через простую программу фильтра, которая смотрит на директивы # line "file" и удаляет текст из #include ed файлов:

clang main.cpp -E|remove_included_code

remove_included_code может быть легко реализован в python, bash, C/С++ и т.д. Вот реализация С++ (С++ был выбран так, чтобы его можно было легко использовать в Windows):

remove_included_code.cpp

#include <iostream>
#include <cstdlib>

using namespace std;
typedef std::string Str;

bool isLineDirective(const Str& line)
{
    return line.size() >= 3
        && line[0] == '#'
        && line[1] == ' '
        && isdigit(line[2]);
}

Str getFile(const Str& line)
{
    const Str::size_type start = line.find_first_of('"') + 1;
    const Str::size_type end = line.find_first_of('"', start);
    return line.substr(start, end - start);
}

int main()
{
    Str line;
    getline(cin, line);
    if ( !isLineDirective(line) ) {
        cerr << "Error: Input must start with a '# line \"file\"' directive\n";
        exit(1);
    }

    const Str startFile = getFile(line);
    Str currentFile = startFile;
    while ( getline(cin, line) ) {
        if ( isLineDirective(line) )
            currentFile = getFile(line);
        else if (currentFile == startFile )
            cout << line << endl;
    }

    return 0;
}

Преимущество этого решения в том, что он будет работать со всеми компиляторами.

Ответ 2

edit: В этом случае я не вижу ничего особенного в прекомпилированных заголовках - любые средства условного предотвращения расширения #include будут делать.

Я достигаю этого для стандартного заголовка, включает в себя упаковку в ifdef, так что препроцессорный вывод содержит #include "foo", но не содержимое. Например,

#ifdef PRECOMPILE
#define TMP #include <stdio.h>
TMP
#undef TMP
#else
#include <stdio.h>
#endif

Затем передайте -DPRECOMPILE вместе с -E.

Затем обработанный вывод содержит нерасширенный #include. Я считаю это более полезным, чем более простая альтернатива обертывания #include в #ifdef при чтении предварительно обработанного вывода.

В первую очередь, это означает, что я могу запустить макро-тяжелый код через препроцессор, а затем clang-format перед компиляцией, как обычно, для значительно улучшенного отладочного опыта.