Clang API. Начало

    Сейчас с уверенностью можно утверждать, что времена самописных C++-парсеров постепенно отходят в прошлое. На сцену медленно, но неумолимо выходит clang — полноценный C++-фронренд и компилятор, предоставляющий своим пользователям богатое API. С помощью этого API можно распарсить исходный текст на C/C++/Objective C, и вытащить из него всю необходимую информацию — от простого лексического значения токенов, до таблицы символов, AST-деревьев и результатов статического анализа кода на предмет всяких разных проблем. В связке с llvm и при сильном на то желании C++ можно использовать в качестве скриптового языка, парся и исполняя C++-программы «на лету». В общем, возможности перед программистами открываются богатые, надо только понять — как ими правильно воспользоваться. А тут, как это не редко случается, и начинается самое интересное.

    1. Clang или clang-c?


    Надо начать с того, что разработчики clang'а предоставляют своим клиентам два вида API. Первое — полностью «плюсовое», но… потенциально нестабильное (в том смысле, что может меняться от версии к версии). Второе — гарантированно стабильное, но… чисто «сишное». Выбор в пользу того или другого надо делать по ситуации, и исходя из потребностей разрабатываемого на базе clang продукта.

    1.1 clang-c API

    В дереве исходников clang'а реализация этой библиотеки размещена в ветке tools (реализация самого ядра clang'а размещена в lib). Эта библиотека компилируется в динамически-подгружаемый модуль, и ее интерфейс предоставляет клиенту ряд гарантий:
    1. Стабильность и backward compatibility. Клиент может безопасно для себя (то есть для своего кода) переходить с одной версии clang'а на другую, не боясь что что-нибудь отвалится или, того хуже, перестанет собираться.
    2. Есть возможность в рантайме определить capabilities используемой реализации clang, и под них подстроиться.
    3. Высокая отказоустойчивость — фатальные ошибки в ядре clang'а не приведут к краху клиента.
    4. Собственные средства управления потоками для тяжеловесных активностей (как то парсинг).
    5. Нет необходимости компилировать сам фронтенд, поскольку весь функционал компилятора и API собран в виде одной динамически-подгружаемой библиотеки.

    Но, за перечисленные преимущества надо платить. По этому clang-c API обладает следующим набором недостатков:
    1. Дизайн в соответствии с паттерном «корабль в бутылке». Сущности, с которыми взаимодействует клиент этого API — по сути своей, обертки над оригинальными классами, предоставляемыми clang API.
    2. (как следствие) Ручное управление ресурсами. Для удобного использования из C++-кода необходимо написать обертки, обеспечивающие RAII.
    3. Очень «узкий» интерфейс. Клиенту предоставляется небольшой набор C-методов и типов, посредством которых он взаимодействует с ядром.
    4. (как следствие) достаточно небогатый набор функционала. Многие средства, предоставляемые clang API, просто недоступны клиенту, или предоставляются в урезанном виде.

    Использовать этот вариант API имеет смысл в тех случаях, когда имеющийся набор «плюсов» существенен для клиентского кода. Ну или «минусы» не столь принципиальны. Это API вполне подходит для извлечения семантической информации из исходного текста (как в виде AST, так и в виде семантической нагрузки каждого конкретного токена в исходном тексте), его нечастой индексации, верификации «на лету» со сбором всей диагностики, и т. п. задач. Соответственно, это подходит для различного рода standalone-трансляторов, генераторов метаинформации, статических анализаторов и верификаторов кода, и т. п.
    И, в свою очередь, это API плохо подходит для задач, где требуется повышенная производительность, или более плотное взаимодействие с ядром компилятора.

    1.2 clang API

    Этот вариант API — по сути своей интерфейс самого ядра компилятора. Это API чисто C++-ное, и предоставляет широкий доступ ко всем возможностям ядра clang'а. К его достоинствам можно отнести:

    1. Как уже было сказано, прямой и удобный доступ ко всем возможностям компилятора.
    2. Удобный (как минимум по сравнению с clang-c) интерфейс.
    3. Большое количество всевозможных кастомизаций.
    4. Несколько более высокая производительность (по сравнению с clang-c).

    И, отчасти как следствие, недостатки:
    1. Незащищенность клиента от возможных падений внутри ядра.
    2. Отсутствие гарантий backward compatibility по интерфейсу.
    3. Поставка в виде статических библиотек. Клиент вынужден линковаться непоредственно с ядром и, как следствие, собирать clang и llvm для своей конфигурации.
    4. «Многословность». В ряде сценариев исходного кода (по сравнению с clang-c API) получается больше.
    5. Далеко не все документировано.
    6. Высокая степень связности с llvm API. Без llvm использовать clang нельзя. С этим нужно просто смириться.

    Насколько существенны недостатки, и перевешивают ли они достоинства — решать надо по ситуации. На мой взгляд, этот вариант использования clang'а надо выбирать везде, ге требуется хорошая производительность или доступ к специфическим возможностям, недоступным посредством clang-c. В частности, при использовании clang'а в качестве on-the-fly-парсера для IDE имеет смысл использовать именно этот вариант API.

    2. Getting started, или парсинг исходного текста


    А зачем тогда, спрашивается, вообще этот clang нужен? Действительно, распарсить исходный текст с целью извлечения из него интересующей разработчика информации или для превращения его в байт-код — это основная задача clang frontend. И для решения этой задачи clang предоставляет богатые возможности. Наверное даже слишком богатые. Признаться, я слегка опешил, когда в первый раз открыл один из примеров к clang'у — производимые в нем манипуляции показались мне из области черной магии, поскольку не совпадали с интуитивными представлениями о том, как этот самый парсинг должен бы выглядеть. В итоге, все оказалось вполне логичным, хотя установка опций парсинга путем передачи массива строк, описывающего аргументы командной строки, меня до сих пор несколько обескураживает.

    2.1 Парсинг с помощью clang-c

    Если бы мое знакомство с clang началось бы с примера, построенного на базе этого API, удивлений было бы меньше. Фактически, парсинг файла производится двумя вызовами. Первый создает экземпляр объекта CXIndex, второй — инициирует собственно разбор исходного текста и построение AST. Вот как это выглядит в исходном тексте:

    #include <iostream>
    #include <clang-c/Index.h>
    int main (int argc, char** argv)
    {
     CXIndex index = clang_createIndex (
             false, // excludeDeclarationFromPCH
             true   // displayDiagnostics
     );
     CXTranslationUnit unit = clang_parseTranslationUnit (
             index,                           // CIdx
             "main.cpp",                      // source_filename
             argv + 1 ,                        // command_line_args
             argc - 1 ,                        // num_command_line_args
             0,                                // unsave_files
             0,                                // num_unsaved_files
             CXTranslationUnit_None           // options
     );
     if (unit != 0 )
             std::cout << "Translation unit successfully created" << std::endl;
     else
             std::cout << "Translation unit was not created" << std::endl;
     clang_disposeTranslationUnit(unit);
     clang_disposeIndex(index);
    }
    


    Первый метод (clang_createIndex) создает контекст, в рамках которого будут создаваться и парситься экземпляры единиц трансляции (CXTranslationUnit). Он принимает два параметра. Первый (excludeDeclarationsFromPCH) управляет видимостью объявлений, прочитанных из precompiled-заголовка в процессе обхода полученного AST. Значение 1 означает, что такие объявления будут исключены из итоговой AST. Второй параметр (displayDiagnostics) управляет выводом диагностики, полученной в процессе трансляции, на консоль.
    Второй метод (clang_parseTranslationUnit) выполняет собственно парсинг файла с исходным текстом. Этот метод имеет следующие параметры:
    • CIdx — указатель на контекст, созданный с помощью вызова clang_createIndex.
    • source_filename — путь к файлу, который необходимо распарсить.
    • command_line_args — аргументы командной строки, которые будут преобразованны в опции компилятора.
    • num_command_line_args — количество аргументов в командной строке, переданной в качестве предыдущего параметра.
    • unsaved_files — коллекция файлов, актуальное содержимое которых находится в памяти, а не на диске.
    • num_unsaved_files — количество элементов в коллекции незаписанных файлов.
    • options — дополнительные параметры парсинга.

    Как можно видеть, вся настройка парсера выполняется посредством передачи парсеру аргументов командной строки в текстовом виде. Параметр unsaved_files полезен в сценариях использования clang'а из редакторов или IDE. Посредством него можно передать парсеру те файлы, которые были модифицированны пользователем, но еще не сохранены на диск. Это коллекция структур типа CXUnsavedFile, содержащих имя файла, его содержимое и размер содержимого в байтах. Имя и содержимое задается в виде C-строк, а размер — в виде беззнакового целого.
    Последний параметр (options) — это набор следующих флагов:
    • CXTranslationUnit_None — тут всё очевидно. Никаких специальных опций парсинга не устанавливается.
    • CXTranslationUnit_DetailedPreprocessingRecord — установка этой опции указывает на то, что парсер должен будет генерировать детальную информацию о том, как и где в исходном тексте применяется препроцессор. Как явствует из документации, опция редкоиспользуемая, приводит к расходу большого количества памяти, и устанавливать её стоит только в тех случаях, когда такая информация действительно требуется.
    • CXTranslationUnit_Incomplete — установка этой опции указывает на то, что обрабатывается не полная (не законченная) единица трансляции. Например, заголовочный файл. В этом случае транслятор не будет пытаться инстанцировать шаблоны, которые должны были бы быть инстанцированы перед завершением трансляции.
    • CXTranslationUnit_PrecompiledPreamble — установка этой опции указывает на то, что парсер должен автоматически создавать precompiled header для всехзаголовочныхфайлов, которые включаются в начале единицы трансляции. Опция полезная в случае, если файл будет часто репарсится (посредством метода clang_reparseTranslationUnit), но со своими особенностями, которые будут описаны в следующем разделе.
    • CXTranslationUnit_CacheCompletionResults — установка этой опции приводит к тому, что после каждого последующего репарсинга часть результатов code completion будет сохранятся.
    • CXTranslationUnit_SkipFunctionBodies — установка этой опции приводит к тому, что в процессе трансляции не будут обрабатываться тела функций и методов. Полезно для быстрого поиска объявлений и определений тех или иных символов.

    Флаги могут комбинироваться посредством операции '|'.

    Два последних метода (clang_disposeTranslationUnit и clang_disposeIndex) удаляют ранее созданные хэндлы, описывающие единицу трансляции и контекст.
    Для успешной сборки этого примера кода достаточно подключить библиотеку libclang.

    2.1 Парсинг с помощью clang API

    Аналогичный (по функционалу) код с использованием clang API выглядит следующим образом:

    #include <vector>
    #include <iostream>
    #include <clang/Basic/Diagnostic.h>
    #include <clang/Frontend/DiagnosticOptions.h>
    #include <clang/Frontend/CompilerInstance.h>
    #include <clang/Frontend/CompilerInvocation.h>
    #include <clang/Frontend/Utils.h>
    #include <clang/Frontend/ASTUnit.h>
    int main(int argc, char ** argv)
    {
     using namespace clang ;
     using namespace llvm ;
     // Initialize compiler options list
     std::vector< const char *> args;
     for (int n = 1; n < argc; ++ n)
             args.push_back(argv[n]);
    
     args.push_back("main_clang.cpp" );
     const char** opts = &args.front();
     int opts_num = args.size();
    
     // Create and setup diagnostic consumer
     DiagnosticOptions diagOpts;
     IntrusiveRefCntPtr< DiagnosticsEngine> diags(CompilerInstance::createDiagnostics(
             diagOpts, // Opts
             opts_num, // Argc
             opts,     // Argv
             0,         // Client
             true,     // ShouldOwnClient
             false     // ShouldCloneClient
     ));
    
     // Create compiler invocation
     IntrusiveRefCntPtr< CompilerInvocation> compInvoke = clang::createInvocationFromCommandLine(
             makeArrayRef(opts, opts + opts_num), // Args
             diags                                // Diags
     );
     if (!compInvoke) {
             std::cout << "Can't create compiler invocation for given args" ;
             return -1;
     }
     // Parse file
     clang::ASTUnit *tu = ASTUnit ::LoadFromCompilerInvocation(
             compInvoke.getPtr(), // CI
             diags,               // Diags
             false,               // OnlyLocalDecls
             true,                // CaptureDiagnostics
             false,               // PrecompilePreamble
             TU_Complete,         // TUKind
             false                // CacheCodeCompletionResults
     );
     if (tu == 0 )
             std::cout << "Translation unit was not created" ;
     else
             std::cout << "Translation unit successfully created" ;
     return 0;
    }
    


    Букв в нём гораздо больше, а для сборки требуется ликовка со следующим набором библиотек: clangLex, clangBasic, clangAST, clangSerialization, clangEdit, clangAnalysis, clangFrontend, clangSema, clangDriver, clangParse, LLVMCore, LLVMMC, и LLVMSupport. При сборке под Windows также требуется добавить advapi32 и shell32. Зато на выходе получится исполняемый модуль без лишних внешних зависимостей.
    Представленный выше код можно разделить на четыре части:
    1. Формирование коллекции параметров командной строки компилятора. В этом варианте API путь к файлу, который требуется разобрать, также передается в качестве одного из элементов коллекции, по этому в данном случае передавать напрямую argv и argc нельзя.
    2. Создание экземпляра Diagnostic Engine. Объект этого класса отвечает за сбор и хранение всех сообщений об ошибках, предупреждений и прочей диагностики, которые могут быть сформированы парсером в процессе разбора исходного текста.
    3. Создание экземпляра Compiler Invocation.
    4. Собственно парсинг исходного текста.


    Формирование коллекции аргументов командной строки

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

    Создание Diagnostic Engine

    Создание DE необходимо для того, чтобы получать от парсера clang различную диагностическую информацию, которую он генерирует в процессе разбора исходного текста. Такие параметры, как максимальное количество отображаемых ошибок, какие именно ошибки/предупреждения отображать и т. п. DE берет из командной строки, которые передается вторым и третьим параметрами. Последние три параметра описывают «диагностического клиента». Это специальный класс, которому DE будет передавать сообщения парсера (по мере их возникновения) для дальнейшей обработки специфичрым для пользователя clang образом. DE может взять контроль за временем жизни клиента на себя, либо работать с клоном переданного объекта. Это позволяет использовать разные сценарии реализации клиента — в виде статического/автоматического объекта, в виде объекта в куче, как часть класса, в методах которого производится работа с clang API, и т. п.

    Создание Compiler Invocation

    На этом шаге, фактически, создается контекст, в рамках которого будет производиться парсинг. Анализируются все параметры переданной командной строки, переменные окружения, создается вся внутренняя инфраструктура (в соответствии с этими параметрами), подключается Diagnostic Engine. После этого clang полностью готов к парсингу того файла, который был передан в качестве последнего параметра.

    Парсинг исходного текста

    Он осуществляется посредством вызова одного из статических методов класса clang:ASTUnit. Таких методов несколько, они заточены под разные сценарии. В примере приведен один из возможных вариантов. В данном случае парсеру передается экземпляр compiler invocation (парсер его потом сам удалит!), экземпляр Diagnostic Engine (его парсер автоматически удалять не будет), и несколько параметров, контролирующих поведение парсера:
    • OnlyLocalDecls — в итоговую AST будут включены только декларации из той единицы трансляции, которая парсилась. Декларации из PCH и подключенных заголовочных файлов будут исключены.
    • CaptureDiagnostic — управляет способом сбора диагностики. Если в качестве этого параметра передано false, то вся собранная диагностика будет передаваться diagnostic client'у, указанному при создании Diagnostic Engine. В противном случае диагностика будет сохранятся во внутренних структурах ASTUnit'а.
    • PrecompilePreamble — как уже говорилось выше, при включенной этой опции парсер будет автоматически создавать PCH для всех включенных в исходный текст заголовков. Да, действительно. Полезно при повтороном парсинге. Но, как оказалось, тут есть некоторые не совсем приятные моменты. Во-первых, фактически PCH создается при первом вызове метода ASTUnit::Reparse для полученного экземпляра ASTUnit'а. Во-вторых, в случае, если производится разбор заголовочного файла с #ifdef-guard'ами, то, увы, ничего не будет создано.
    • TUKind — тип единицы трансляции. Тут возможны следующие варианты:
      • TU_Complete — парсится полностью завершенная единица трансляции. В этом случае в итоговую AST будут помещены также все инстансы шаблонов, используемых в исходном тексте.
      • TU_Prefix — парсится «префикс для единицы трансляции», в этом случае исходный текст не считается завершенным.
      • TU_Module — парсится некий «модуль». Что это такое — документация умалчивает.
    • CacheCodeCompletionResults — в процессе разбора будут закешированы результаты code completion. Действительно помогает при последующих запросах code completion.


    3. Маленькие хитрости в наборе опций


    В своих первых экспериментах (это был парсинг заголовочных файлов на предмет извлечения деклараций) я долго не понимал причину, по которой парсинг завершался с большим количеством ошибок. В итоге все оказалось довольно просто. Итак, опции, которые могут оказаться полезными:
    • -x language — указывает конкретный тип файла, который парсится. Совместима с аналогичной опцией компилятора gcc.
    • -std=standard — указывает стандарт, которому соответствует исходный текст. По значениям совместима с аналогичной опцией компилятора gcc.
    • -ferror-limit=N — устанавливает в N максимальное количество ошибок, после которого парсинг будет завершен. Если требуется распарсить файл полностью игнорируя любые ошибки, то N должно быть равно 0.
    • -include <prefix-file> — указывает файл (обычно заголовочный) который должен быть распарсен до начала парсинга основного файла. Вообще, эта опция изначально предназначена для подключения PCH-заголовка, но при разборе файлов может быть полезна для, например, определения различных макросов.


    На этом первое знакомство с clang API можно считать законченным. Подробнее про clang-c API можно почитать на официальном сайте clang: clang.llvm.org/doxygen/group__CINDEX.html
    Там же можно ознакомится со всей иерархией классов clang API. К некоторому сожалению, документация генерируется автоматически из апстрима clang, по этому сигнатуры функций, их набор и т. п., описанные на сайте с документацией, могут отличаться от тех, которые представлены в том или ином релизе.

    В следующей заметке я расскажу о том, как из созданной с помощью clang AST можно получить дерево деклараций.
    Поделиться публикацией
    Комментарии 40
      +4
      А еще clang-c можно наверное юзать для автоматической генерации биндингов к скриптовым языкам
        0
        Да его много для чего юзать можно.
        +2
        Я думаю будет в тему добавить ссылку на доклад от Chandler Carruth — Refactoring C++ with Clang.
        www.youtube.com/watch?v=yuIOGfcOH0k&feature=plcp
        В нем объясняется и показывается на конкретном примере реализация ренейма метода.
          +1
          Ещё б они заопенсорсили свой рефкторинговый тул…
            0
            ASTMatchers некоторое время назад закоммитили.
          0
          Думаю стоит добавить эту статью ещё в хаб Компиляторы.
          0
          Объясните мне пожалуйста, в чем смысл использования С++ как скриптового языка? В какой ситуации вообще такое решение может быть принять и быть оправданным?
            0
            *принято, selffix
              +1
              Возможные области примерения, которые я вижу, две:
              1. Трансляторы (кодогенераторы) C++ -> C++ типа Qt moc.
              2. Лёгкие плагины к IDE, написанных на C++.

              Мотивация примерно следующая. В обоих случаях основная кодовая база — на C++. Чтобы не расширять набор языков, для которых требуется поддержка (а любой скриптовый язык этот набор автоматически расширяет), «скриптование» непосредственно на C++ имело бы смысл.
                0
                Когдогенераторы — хорошее применение, но в виде скрипта сомнительно. В IDE проще python/jscript использовать.
                  0
                  Вопрос комфорта, на мой взгляд.
                    +1
                    Ну и да. Вопрос последующей поддержки. Чем большее количество языков используется в исходниках — тем большее количество разноплановых специалистов нужно, чтобы поддерживать кодовую базу. В итоге нередко получается, что знания о том, как работает ммм… скрипт для генерации ну, скажем, метаинформации по набору классов (работающий на связке doxygen + msxslt + javascript), превращаются в сакральные вместе с уходом из команды человека, который этот скрипт поддерживал.
                      0
                      Я согласен, но не проще ли это реализовать в виде уже скомпилированной DLL, а не on-the-fly в виде плагина в исходниках. На мой взгляд, это поможет избежать кучи проблем.
                        0
                        Всё зависит от того, какая политика выбирается в отношении хранения кодовой базы и инструментария, возможно ли кодогенераторы собирать вместе с регулярной сборкой продукта, сколько телодвижений придётся делать другим разработчикам, чтобы всем этим тулчейном пользоваться на рабочих машинах, и т. п.
                          0
                          ОК, принято.
                  +1
                  Я думаю, можно значительно расширить возможности отладчика в IDE. Начиная с выполнения кода при его изменении без перекомпиляции/перезапуска отладчика.
                    0
                    Это хорошая идея. Мне нравится.
                  0
                  Можно, пожалуйста, примеры обработки распарсенного исходника?
                    +1
                    Будут, но чуть позже. Иначе статья вышла бы уж слишком большой.
                    0
                    Раньше в clang была возможность конвертирования С++ кода в С. Интересно, возможностей API хватит для реализации этого?
                      0
                      Если есть конвертация в байткод, то его, по идее, можно транслировать в си. Это даже легче, чем машинный код в си.
                        0
                        Си бекенд удалили так как не было мейнтейнеров.
                        0
                        Используйте Tooling для работы с clang api, пожалуйста! Потому что следующий вопрос после «а как это скомпилировать» обычно «а почему системные заголовки не подключаются» — потому что в вашем примере вы фактически продублировали маленький кусочек драйвера, который делает очень много системно-зависимой магии за кулисами.
                          0
                          Если разобраться, то clangTooling не делает ничего особенного. Тут ведь как — основная работа по разбору опций командной строки сосредоточена в библиотеке clangDriver. И она используется из clangFrontend при создании экземпляра CompilerInvocation. Собственно, результаты этой работы не раз видел в отладчике.
                            0
                            Я говорю не про разбор опций, а про формирование опций для -cc1 режима. Например, поиск инсталляции GCC и стандартной библиотеки c++.

                            А так да — ничего особого, только делает ваш код короче раз в пять.
                              0
                              Задам вопрос так. А вы пытались откомпилировать приведенные примеры и проверить — находятся ли, например, пути к стандартным заголовкам или нет? Может быть, конечно, я недостаточно внимательно посмотрел исходники Tooling, но у меня сложилось впечатление (и комментарии к классам это впечатление усилили), что основное ее назначение — позволить клиентскому коду обработать за один раз сразу несколько исходных файлов. И никаких специальных. Впрочем, я мог и ошибиться, и не туда посмотреть.
                                0
                                > А вы пытались откомпилировать приведенные примеры и проверить — находятся ли, например, пути к стандартным заголовкам или нет?

                                Не пробовал :) Вот был бы патч на дерево кланга…

                                Сейчас вчитался в код — вы используете ASTUnit, поэтому вполне возможно ваш код будет находить стандартную библиотеку. Извините если был не прав.

                                > основное ее назначение — позволить клиентскому коду обработать за один раз сразу несколько исходных файлов

                                В том числе. А также избавить клиентский код от необходимости разбираться в хитросплетениях объектов, которые ему непосредственно не нужны.
                                  0
                                  По поводу хитросплетений объектов — интерфейс кланга настолько широкий, что позволяет выполнить одно и то же действие несколькими различными способами. И у каждого варианта будут свои особенности.
                                    0
                                    разбираю сейчас примеры. у меня на os x без дополнительного шаманства не данный пример c++ include не подхватывает, ни через tooling.
                            +1
                            Где же, где же следующая статья?
                              0
                              Будет ли продолжение? :)
                                0
                                Да. Продолжение будет. Постараюсь к концу января — началу февраля выложить.
                                  0
                                  Круто! Я сейчас активно изучаю эту тему, будет интересно почитать :)
                                    0
                                    Вот уже и начало февраля, а статьи все нет и нет…
                                  0
                                  Прошу прощение, за оживление старого топика.
                                  Столкнулся с тем, что не могу заставить Ваши примеры работать
                                  #include "clang/Frontend/DiagnosticOptions.h"
                                  
                                  — этот файл я могу найти только в Basic, но не во Frontend,

                                  IntrusiveRefCntPtr< DiagnosticsEngine> diags(CompilerInstance::createDiagnostics(
                                  
                                  — эта строчка выдает No matching function for call to 'createDiagnostics'

                                  не знаю почему так. куда посоветуете копать и что можно читать про clang-c API, кроме доков?
                                    0
                                    API существенно поменялся =(
                                      0
                                      clang C++ API (в отличие от C-шного libclang) не обременено обратной совместимостью. Поэтому от версии к версии может изменяться. Иногда — существенно.
                                      0
                                      А можно услышать несколько основных ограничений Clang C API? Просто обратная совместимость тоже неплохой аргумент и только тем фактом, что это Pure C API я вполне могу смириться. Но какую информацию я при этом потеряю и не смогу получить от clang?
                                        0
                                        Думаю, да. Сейчас занимаюсь подготовкой доклада, где аккурат этот вопрос будет освещаться. По результатам исследования — отпишусь.

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                      Самое читаемое