Могу ли я получить AST от cppyy - PullRequest
1 голос
/ 17 апреля 2020

Я хочу получить доступ к AST в cppyy до создания привязок python. Я хотел бы использовать это для генерации других видов привязок.

Я видел cppyy-generator, но он требует отдельной установки clang на машине. Поскольку cppyy может выполнять JIT-компиляцию без отдельной установки clang, я должен верить, что AST доступен из базового интерпретатора cling. Есть ли способ получить эту информацию AST от cppyy?

пример:

import cppyy

cppyy.cppdef("""
namespace foo
{
class Bar
{
public:
    void DoSomething() {}
};
}

""")

cppyy может (удивительно) сгенерировать cppyy.gbl.foo.Bar для меня. Это означает, что он должен был использовать Cling для компиляции, получения AST и генерации python. Как я могу увидеть данные AST?

Спасибо!

Редактировать :

Я вижу, что большая часть необходимой мне информации находится в cppyy файлы backind capi и cpp_cppyy. Тем не менее, мой CPython foo недостаточно силен, чтобы выяснить, как их вызывают и как я могу получить к ним доступ из скрипта python.

Edit2 :

В настоящее время мы используем комбинацию типов xml и pyg ccxml для создания python структуры данных, представляющей AST. Я вижу много совпадений с тем, что cppyy и wi sh уменьшают зависимости только до cppyy, поскольку мы уже используем его для других целей, и он прекрасно автономен.

Мы используем данные AST для нескольких вещей. Важным является генерация кода. Поэтому мы хотели бы повторить AST намного , как вы можете с помощью pyg ccxml.

1 Ответ

2 голосов
/ 17 апреля 2020

Здесь есть пара неясностей b / c одни и те же имена относятся к разным шагам и в разных местах. Позвольте мне объяснить структуру (и историю), которая может даже ответить на ваш вопрос.

cppyy-generator использует привязки Clang Python. Таким образом, AST, к которому он обращается, - это C ++, и он доступен во всей своей (безобразной) красоте. Вам не нужна какая-либо часть cppyy, чтобы использовать привязки Clang Python. cppyy-generator служит для конкретного c варианта использования, в котором вы хотите, чтобы все локальные объекты C ++ были предварительно загружены в модуль Python. Поскольку cppyy использует ленивые все и автоматическую загрузку, по соображениям производительности концепция «всех сущностей C ++» (локальная или иная) не имеет четко определенного значения. Следовательно, был использован libclang, где концепция ясна.

cppy-backend capi (или C -API), это API, который был разработан в целях сокращения для обслуживания PyPy-реализации cppyy. Это C стиль API для bootstrap cppyy / C ++. Это сводится к основам написания привязок Python -C ++, скрывая многие нерелевантные детали Clang AST (например, 15 или около того способов, которыми шаблон может существовать в Clang AST, сокращены до «IsTemplate» et c. ). Серверная часть C -API никоим образом не зависит от Python и не использует его вообще.

Реализация серверной части C -API выглядит довольно не красиво. Частично из-за исторических c причин (плохо), частично для того, чтобы скрыть весь Cling и, следовательно, Clang, чтобы предотвратить столкновения с другими частями приложения, которые могут использовать Clang или LLVM (хорошая вещь; версия Clang, используемый Cling, настроен и может не работать, например, для Numba). Опять же, все это полностью независимо от того, что связано с Python.

Затем его использование в Python. Существует две разные реализации: CPyCppyy для CPython, который реализован в C, и модуль PyPy _cppyy, который реализован в R Python. Оба выполняют заклинания для перехода из Python в C ++ через C -API. Ни один из них не генерирует и не использует Python AST: одновременно генерирует и манипулирует объектами Python. Это происходит лениво. Подумайте, как это сделать: пользователь Python в приведенном выше примере напечатает что-то вроде cppyy.gbl.foo.Bar().DoSomething(). В cppyy Python __getattr__ используется для перехвата имен, а затем он просто проходит через бэкэнд к Cling, чтобы спросить, знает ли он, что такое foo, Bar et c. Например, C -API GetScope("foo") вернет действительный идентификатор, поэтому CPyCppyy / _cppyy знает, как сгенерировать класс Python для представления пространства имен. Однако ни в коем случае он не сканирует глобальное (или даже foo) пространство имен в AST полностью, чтобы априори генерировать привязки. Исходя из вашего описания, в CPyCppyy / _cppyy нет ничего, что вам пригодилось бы.

Чтобы вернуться к первому утверждению о том, что вы хотите создать другие типы привязок. Вы не указываете, какой тип привязок, но основной причиной перехода на C -API является то, что он находится поверх Cling, а не Clang как Clang AST непосредственно из C ++ или через его Python привязки было бы. Cling предлагает легкий доступ к JIT, но вы также можете программировать его непосредственно из Clang (его библиотек, а не AST). В качестве примера такого легкого доступа в бэкенде C -API вы можете просто сбросить строку C ++ для JITted в функцию compile (которая делает то же самое, что и cppdef в вашем примере). Люди из Cling планируют предоставить лучший интерфейс для динамических c языков непосредственно из Cling, но эта работа еще не завершена (AFAIK).

Наконец, обратите внимание, что Cling содержит Clang, поэтому, если вы установите Cling, вы все равно получите Clang (и LLVM), что может быть серьезной зависимостью.

EDIT : По сути, остается, что В отличие от этих других инструментов, cppyy не предлагает ни списка начальных точек (например, «все классы»), ни полного / истинного AST. Вы можете скопировать заголовок cpp_cppyy.h из бэкэнда (он не является частью установки), просто включить его и использовать его (все символы уже экспортированы), но вам необходимо заранее знать список классов. Пример:

import cppyy

cppyy.cppdef('#define RPY_EXPORTED extern')
cppyy.include('cpp_cppyy.h')

import cppyy

cppyy.cppdef("""
namespace foo {
class Bar {
public:
    void DoSomething() {}
};
}""")

cpp = cppyy.gbl
capi = cpp.Cppyy

scope_id = capi.GetScope(cpp.foo.Bar.__cpp_name__) # need to know existence
for i in range(capi.GetNumMethods(scope_id)):
     m = capi.GetMethod(scope_id, i)
     print(capi.GetMethodName(m))

Но, как вы можете видеть, он не дает однозначного результата с исходным кодом. Например, все сгенерированные компилятором конструкторы и деструктор перечислены как методы.

На самом деле в бэкэнд-API нет ничего такого, как run_functions = unittests.member_functions('run'), как в документации pyg ccxml, которая Вы ссылку. Причина в том, что это не имело никакого смысла в контексте cppyy. Например, что если в другой заголовок загружено больше функций run? Что если это шаблонная функция и появляется больше экземпляров? Что, если в последующем коде появится using namespace ..., добавив больше перегрузок run?

cppyy имеет эту функцию GetAllCppNames C -API, но она не является исчерпывающей. Он существует для дополнения табуляцией в редакторах кода (он вызывается в настраиваемых __dir__ функциях связанных областей). На самом деле, именно потому, что это не было завершено, cppyy-generator использует libclang.

Вы упоминаете gInterpreter в комментариях, но это часть истории, которую я упоминал ранее: это злополучный промежуточный продукт между полным AST, предлагаемым libclang, и минимальным c, необходимым для Python (таким как бэкэнд C -API). Да, вы можете использовать его напрямую (на самом деле он все еще используется под бэкэндом C -API), но это намного более неуклюже, но мало пользы.

Например, для обработки этого "получения Например, вы можете сделать все "методы запуска":

import cppyy

cppyy.cppdef("""
namespace foo {
void run(int) {}
void run(double) {}
}""")

cpp = cppyy.gbl

# using the ROOT/meta interface
cls = cpp.TClass.GetClass(cpp.foo.__cpp_name__)
print('num "run" overloads:"', cls.GetListOfMethodOverloads('run').GetSize())

# directly through gInterpreter
gInterp = cpp.gInterpreter

cls = gInterp.ClassInfo_Factory(cpp.foo.__cpp_name__)
v = cpp.std.vector['const void*']()
gInterp.GetFunctionOverloads(cls, 'run', v)
gInterp.ClassInfo_Delete(cls)

print('num "run" overloads:"', len(v))

Но прежний интерфейс (через TClass) может не остаться, а интерфейс gInterpreter действительно ужасен, как вы можете видеть.

Я почти уверен, что вы не будете счастливы, пытаясь заставить cppyy заменить использование pyg ccxml, и на вашем месте я бы использовал привязки Clang Python вместо

...