В этом посте будут рассмотрены некоторые продвинутые варианты применения Cling, поддерживающие интероперабельность и расширяемость. Здесь мы постараемся продемонстрировать создание экземпляров по запросу; встраивание Cling как услуги, а также похвастаемся расширением, обеспечивающим автоматическое дифференцирование на лету. Cling реализует механизм  LookupHelper, который берет код C++ и проверяет, существует ли уже объявление с именем, которое так квалифицировано. Например:  В данном конкретном случае findScope создает экземпляр шаблона и возвращает его представление в виде абстрактного синтаксического дерева для clang. Инстанцирование шаблона по требованию решает распространенную библиотечную проблему, которая называется «комбинаторный взрыв шаблона» (template combinatorial explosion). Инстанцирование шаблона по требованию и преобразование текстовых квалифицированных имен C++ в метаинформацию об объекте показало себя как очень мощный механизм, способствующий сериализации данных и языковой интероперабельности. В качестве примера рассмотрим генератор cppyy, предоставляющий во время выполнения автоматические привязки кода Python к коду на C++, при помощи Cling. Python сам по себе – это динамический язык, выполняемый интерпретатором. Поэтому взаимодействие с кодом C++ получается более естественным, если в качестве промежуточного звена выступает Cling. Среди примеров – инстанцирование шаблонов во время выполнения, обратные вызовы функций (указателей), межъязыковое наследование, автоматическое понижающее приведение (downcasting) и сопоставление исключений (exception mapping). Многие продвинутые возможности C++, такие как размещающий оператор new (placement new), множественное виртуальное наследование, шаблоны с переменным количеством параметров (variadic templates), т.д., естественным образом разрешаются при помощи LookupHelper. cppyy достигает высокой производительности путем «вселенивого» подхода к конструированию привязок времени выполнения и специализации типичных случаев путем отражения времени выполнения. В целом он характеризуется гораздо меньшими издержками при вызовах, чем, например, pybind11, и перебирать std::vector через cppyy гораздо быстрее, чем перебирать массив numpy того же типа. Если сделать еще шаг вперед, то можно обратить внимание на реализацию cppyy для PyPy, полностью совместимого интерпретатора Python, который может похвастаться трассирующим JIT-генератором. Во многих случаях он может предоставить нативный доступ к коду C++ при JIT в стиле PyPy, обеспечивая, в частности, разрешение перегрузок и предоставляя подсказки JIT, благодаря которым возможна агрессивная оптимизация.  Благодаря отражению времени выполнения, действующему в Cling, cppyy значительно упрощает поддержку большого программного стека; кроме собственной привязки к интерпретатору Python, имеющейся в cppyy, этот инструмент не содержит никакого скомпилированного кода, который зависел бы от Python. То есть, модули расширений, основанные на cppyy, не требуют никакой перекомпиляции при смене версий Python (ни даже, если, скажем, мы будем переключаться между интерпретаторами CPython и PyPy). В следующем примере показана плотная интеграция между C++ и Python; здесь видна активная двунаправленная коммуникация между инстанцированием шаблонов и переопределениями, связанными с перекрестным наследованием; еще здесь видны поведения, характерные для времени выполнения (все происходит во время выполнения, скомпилированного кода здесь нет). Вывод: В этом фрагменте кода мы создаем классы Python, основанные на аргументах Python, производные от шаблонного класса C++, экземпляр которого создается с указанием типа. Класс Python предоставляет реализацию защищенной функции, которая вызывается из публичной функции и в результате дает ожидаемое возвращаемое значение, которое выводится на экран. Здесь мы заостряем внимание на следующем:  Python создает классы во время выполнения, что может и Cling, пусть даже они и объявляются в модуле (здесь      релевантные классы создаются в фабричном методе); Шаблонные классы C++ можно инстанцировать на лету прямо из Python, взяв тип аргумента (т.e., воспользовавшись интроспекцией во время      выполнения в Python), чтобы создать базовый      класс C++ для класса Python. Межъязыковое произведение происходит во время выполнения      и не требует от класса С++ никакой поддержки сверх виртуального      деструктора и виртуальных методов; «Защищенные» методы C++ в Python могут быть переопределены,      пусть даже в Python такой концепции нет, и вы, в сущности, не можете      вызывать в Python защищенные методы из связанных объектов C++; Все это работает прямо «из коробки». cppyy используется в нескольких больших базах кода по физике, химии, математике и биологии. Его легко установить через pip из PyPI (https://pypi.org/project/cppyy/), а также при помощи conda. Другой пример – интеграционный язык Symmetry (SIL). Это предметно-ориентированный язык функционального толка на основе языка D. Он был разработан для внутрикорпоративного применения в компании Symmetry Investments, и там и используется. Одна из основных целей SIL – обеспечить легкую интероперабельность со всевозможными языками и системами. Это достигается при помощи разнообразных плагинов. Для вызова кода C++ язык SIL использует плагин под названием sil-cling, работающим как переходник между SIL и Cling. Однако, sil-cling взаимодействует с Cling не напрямую, а через cppyy-бекенд. Это обертка cppyy на C/C++, охватывающая Cling, который предоставляет стабильный API для рефлексии C/C++. Существует два коренных типа, которые предоставляются из sil-cling в SIL. Один из них - CPPNamespace, предоставляющий пространство имен C++ и обеспечивающий свободный вызов функций, доступ к переменным пространства имен и создание экземпляров объектов из тех классов, что определены в этом пространстве имен. Другой - ClingObj, это посредник для объекта C++, обеспечивающий конструирование, вызов метода и манипуляции с членами данных объекта. Притом, что cppyy представляет классы, структуры и пространства имен C++ как «области видимости», и информация о рефлексии любых этих сущностей C++ получается через ассоциированные с ними объекты «области видимости», оба оберточных типа, предоставляемых для SIL, хранят ссылки на ассоциированные с ними объекты области видимости. Такой объект области видимости запрашивается всякий раз, когда оберточные типы используются для вызова кода C++. Все вызовы выполняются из SIL через два оберточных типа, у каждого из которых по 3 аргумента: используемый объект-обертка, имя функции C++, которую нужно вызвать и (если нужно) последовательность аргументов для этой функции. Как только будут выполнены разрешение перегрузок и преобразование аргументов, sil-cling вызовет подходящую функцию cppyy, которая обернет вызов и диспетчеризует его в Cling для динамической компиляции. На момент написания статьи sil-cling может применяться для вызова таких библиотек C++ как Boost.Asio, dlib или Xapian. В следующем примере создается клиент-серверное приложение на основе Boost Asio, написанное на SIL с использованием плагина sil-cling. Server.sil содержит код SIL для сервера. Он начинает работу с включения нужных заголовочных файлов, это делается при помощи cppCompile. На следующем шаге создаются оберточные объекты для тех пространств имен, что нам нужны, и это делается путем вызова cppNamespace с именами тех пространств, к которым нам нужно получить доступ. Эти обертки CPPNamespace применяются для создания экземпляров классов, определяемых внутри тех пространств имен C++, которые в этих обертках заключены. При помощи таких оберток создаются конечная точка, принимающий сокет (который слушает входящие соединения) и активный сокет (обрабатывающий коммуникацию с клиентом). Затем сервер дожидается соединения и, как только клиент подключится, сервер считывает это сообщение и отправляет отклик. Client.sil содержит код SIL для клиента. Поскольку это сервер, он включает релевантные заголовки, создает обертки для требуемых пространств имен и далее для создания конечной точки и сокета. Затем клиент подключается к серверу, отправляет сообщение и ждет, пока сервер ответит. Как только ответ придет, клиент выведет его на экран. Вывод: Cling, точно как и Clang, устроен так, что им можно пользоваться как библиотекой. В следующем примере будет показано, как инкорпорировать libCling в программу на C++. Cling можно использовать по требованию, как услугу – чтобы компилировать, изменять или описывать код на C++. В программе, приведенной в качестве примера, показано несколько способов, которыми могут взаимодействовать скомпилированный и интерпретируемый C++: callCompiledFn – cling-demo.cpp      определяет глобальную переменную aGlobal; статическую переменную для чисел с плавающей      точкой, anotherGlobal; а также соответствующие методы доступа. Аргумент      interp – это созданный ранее экземпляр интерпретатора Cling. Точно как и в стандартном C++, достаточно заранее объявлять скомпилированные      сущности интерпретатору, чтобы потом их можно было использовать. Затем      информация о выполнении от различных вызовов, которые нужно обработать,      хранится в обобщенном объекте Cling Value, который используется для обмена информацией      между скомпилированным и интерпретируемым кодом. callInterpretedFn – дополнительно к callCompiledFn, скомпилированный код может вызвать интерпретируемую      функцию, запросив Cling сформировать указатель      функции на основе заданного искаженного имени. Далее в вызове используется стандартный синтаксис C++. modifyCompiledValue – Cling полностью понимает C++, что позволяет нам поддерживать сложные низкоуровневые      операции над памятью, выделяемой в стеке. В качестве примера запросим у компилятора      адрес локальной переменной loc      в памяти, а затем прикажем      интерпретатору прямо во время выполнения возвести ее значение в квадрат. Вывод: Пересекая границу между компилируемым и интерпретируемым кодом, мы полагаемся на предусмотренную в Clang реализацию двоичного интерфейса (ABI) хост-приложения. С годами эта реализация приобрела большую надежность как в Unix, так и в Windows, хотя, Cling активно используется для взаимодействия с базами кода, скомпилированными при помощи GCC и чувствителен к несовместимостям ABI между GCC и Cling в контексте спецификации Itanium ABI.  Cling, точно как и Clang, поддается расширению плагинами. В следующем примере показан вариант встраиваемого использования расширения Cling для автоматического дифференцирования, этот вариант называется Clad. Clad преобразует абстрактное синтаксическое дерево Clang так, чтобы получить производные и градиенты математических функций. При создании экземпляра Cling мы указываем -fplugin и путь к самому плагину. Затем определяем целевую функцию pow2 и запрашиваем ее производную относительно ее первого аргумента. Вывод: Мы продемонстрировали возможности Cling, позволяющие инстанцировать экземпляры шаблонов по требованию, инкорпорировать интерпретатор в стороннем коде, а также способствовать расширению интерпретатора. Ленивое инстанцирование шаблонов во встраиваемом провайдере может сильно пригодиться в случае, когда нужно обеспечить интероперабельность с C++. Расширяя такой сервис предметно-ориентированными возможностями, можно найти такой нужный выход для решения многих научных задач и более широких вариантов применения. Автор выражает благодарности Сильвейну Корлею (Sylvain Corlay), Симеону Эригу (Simeon Ehrig), Дэвиду Ланжу (David Lange), Крису Латтнеру (Chris Lattner), Хавьеру Лопесу Гомесу (Javier Lopez Gomez), Виму Лаврийсену (Wim Lavrijsen), Акселю Науманну (Axel Naumann), Александру Пеневу (Alexander Penev), Хавьеру Валльсу Пла (Xavier Valls Pla), Ричарду Смиту (Richard Smith), Мартину Васильеву (Martin Vassilev), Иоане Ифрим (Ioana Ifrim), участвовавшим в подготовке этого поста.