Здесь есть пара неясностей 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 вместо