Реализация препроцессора для C на Python


Оглавление (нажмите, чтобы открыть):

Реализация предварительного процессора C в Python или JavaScript?

Существует ли известная реализация препроцессорного инструмента C, реализованного либо в Python, либо в JavaScript? Я ищу способ надежного предварительного создания исходного кода C (и C) и хочу иметь возможность обрабатывать, например, условную компиляцию и макросы, не вызывая внешний инструмент CPP или собственную библиотеку кода. Другим потенциальным вариантом использования является предварительная обработка в веб-приложении в веб-браузере.

До сих пор я нашел реализации в Java, Perl, и, конечно, C и C снова. Возможно, будет возможно использовать один из C для JavaScript, которые теперь становятся доступными.

Инструменты PLY (Python Lex и Yacc) включают cpp, реализованный в Python.

Отъезд PLY, реализация Lex и Yacc на Python, которая содержит инструмент cpp, реализованный в python, используя PLY lexer. Существует также pycparser, парсер, построенный поверх PLY для языка C.

Его также можно найти на PYPI (индекс пакета Python), чтобы его можно было загрузить напрямую с помощью pip.

Если вы хотите использовать инструкции #define и #ifdef для выполнения простой условной компиляции, она должна отвечать вашим потребностям. Вы можете запустить его как автономный script и указать входной файл (который может быть на любом языке) для обработки, или вы можете импортировать его как модуль python и использовать его для самостоятельного потребления источника python.

pypreprocessor поддерживает не:

  • замена макроса
  • логические операции (Ie. || & &)
  • математические операции (Ie. + — */)
  • многострочные директивы препроцессора

Это инструмент, разработанный специально для условной компиляции, а не метапрограммирования.

Вот пример того, как обрабатывать файл C с помощью pypreprocessor в качестве автономного script:

Реализация довольно проста.

defines.append( ‘отладки’):

Имеет такой же эффект, как добавление директивы’dedeine debug ‘в файл. Это просто удобный способ установки определяет.

pypreprocessor.input = ‘input_file.c’:

Устанавливает файл, который вы хотите обработать.

pypreprocessor.output = ‘output_file.c’:

Устанавливает, куда будет выводиться файл.

pypreprocessor.removeMeta = True:

Удаляет все директивы препроцессора, найденные в файле. Сюда входят все #define, #ifdef, #undefine, #else и т.д. Из исходного файла. Это полезно для создания «чистой» производственной версии источника минус все условные коды и директивы, которые не используются. Этот шаг также полностью необязателен. Если вы не добавите этот код, который не попадает в определенное условие, будет автоматически прокомментирован.

Если вам нужна замена макросов, например, сменить источник с одного языка на другой, pyprocessor пока не поддерживает. Теоретически, это не займет много работы, но lexer нужно будет расширить, чтобы пропустить ключевые слова, которые появляются в текстовых строках.

SideNote: он также совместим как с python2x, так и с python3k.

Отказ от ответственности: я являюсь автором pypreprocessor.

Создание препроцессора с Python

Мне в основном нужно написать препроцессор C с Python, я ищу и, поскольку мне нужно полностью настроить свой код и иметь полное представление о том, что происходит, я лучше пишу самостоятельно.

Вот где я нахожусь: сначала я анализирую некоторые заголовочные файлы (.h), чтобы найти ключевое слово #define и создаю словарь со всеми найденными директивами (с их значениями они имеют один). Затем мне нужно проанализировать исходные файлы (.c) в зависимости от директив, которые я нашел ранее. Механизм, который я использую в настоящее время для проверки необходимости обработки кода, заключается в следующем: я беру все имена своих определений и их значения и выполняю exec(«define_name = define_value») (со значением «1», если оно не указано) ). Затем, чтобы разрешить условие, такое как # #if defined DEFINE_1 || defined DEFINE_2 && (DEFINE_3 == 10) . #if defined DEFINE_1 || defined DEFINE_2 && (DEFINE_3 == 10) . Я #if defined DEFINE_1 || defined DEFINE_2 && (DEFINE_3 == 10) . ключевое слово препроцессора C, чтобы сделать их в стиле Python, который будет генерировать DEFINE_1 or defined DEFINE_2 and (DEFINE_3 == 10) .

И, наконец, я использую eval(. ) в этой строке, чтобы узнать результат.

ВОПРОС: мне было интересно, нужно ли использовать exec / eval, и многие ли не хотят их использовать, есть ли лучшее решение?

Конечно, exec() не нужен и не должен использоваться. Я даже не уверен, что вы ожидаете, так или иначе, поскольку это вызовет оболочку для установки переменной, которая будет существовать только в под-оболочке.

И вообще, вы должны избегать операторов eval() так как это редко является правильным решением.

Так что ты можешь сделать?

1) во-первых, поскольку программы могут быть написаны там, где один оператор заменяет предыдущий оператор, вы не можете предварительно обработать файл .h (или даже предположить, что #define находятся только в .h). файлы в первую очередь) и это работает. Учти это:

Если вы предварительно обработаете все, вы установите для «foo» значение 1, затем значение 0, а затем вычислите значение #if позже. Вы не можете сделать это .

2) Более распространенной вещью будет написать синтаксический анализатор, который будет построчно обрабатывать содержимое каждой строки по одному. Таким образом, вы даже можете написать рекурсивную функцию для работы с операторами #include чтобы вы могли начать с простого файла .c и позволить ему извлекать нужные заголовки, которые он использует, вместо того, чтобы указывать их каким-либо другим способом.

В конце концов, вы должны получить что-то вроде (в функции с именем «read_file»):

Очевидно, что если бы я показал вам полное решение (а вышеприведенное вряд ли является красивым кодом, но оно кратко для демонстрации целей), я бы решил вашу проблему (домашнюю работу?) Для вас. Но вышесказанное — лучший способ спроектировать все это, чем путь, по которому вы шли.

Многопроцессорная обработка в Python | Комплект 1 (Введение)

Эта статья — краткое, но краткое введение в многопроцессорность в языке программирования Python.

Что такое многопроцессорность?

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

Почему многопроцессорность?

Рассмотрим компьютерную систему с одним процессором. Если ему назначено несколько процессов одновременно, ему придется прервать каждую задачу и ненадолго переключиться на другую, чтобы все процессы работали.
Эта ситуация похожа на то, как повар работает на кухне один. Он должен выполнить несколько задач, таких как выпечка, помешивание, замес теста и т. Д.

Итак, суть в том, что: чем больше задач вы должны выполнить одновременно, тем сложнее становится отслеживать их все, и соблюдение правильного времени становится все более сложной задачей.
Вот здесь и возникает концепция многопроцессорности!
Многопроцессорная система может иметь:

  • многопроцессорный, то есть компьютер с более чем одним центральным процессором.
  • многоядерный процессор, то есть один вычислительный компонент с двумя или более независимыми фактическими процессорами (называемыми «ядрами»).

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

Это как шеф-повар в последней ситуации, которому помогают его помощники. Теперь они могут делить задачи между собой, и шеф-повару не нужно переключаться между его задачами.

Многопроцессорность в Python

В Python модуль многопроцессорной обработки включает в себя очень простой и интуитивно понятный API для разделения работы между несколькими процессами.
Давайте рассмотрим простой пример использования многопроцессорного модуля:

Реализация препроцессора для C на Python

Здравствуйте, glap, Вы писали:

G>На rsdn проскакивал пост про удобный препроцессор для си-подобных языков на питоне.
G>Прямо в коде комментарием писался код генерации и в этот же файл поступал результат,
G>т.е. не было дублирования исходного кода.
G>Помогите найти, что-то не выходит.

Есть еще https://cpip.sourceforge.net, но это именно препроцессор, cog проще.

От: raydac https://www.igormaznitsa.com
Дата: 22.01.16 09:10
Оценка: 2 (1)
От: flаt
Дата: 22.01.16 09:18
Оценка: 2 (1)
От: glap
Дата: 22.01.16 14:23
Оценка:

Здравствуйте, Conr, Вы писали:

C>Здравствуйте, glap, Вы писали:

G>>На rsdn проскакивал пост про удобный препроцессор для си-подобных языков на питоне.
G>>Прямо в коде комментарием писался код генерации и в этот же файл поступал результат,
G>>т.е. не было дублирования исходного кода.
G>>Помогите найти, что-то не выходит.

C>https://nedbatchelder.com/code/cog ?

C>Есть еще https://cpip.sourceforge.net, но это именно препроцессор, cog проще.

Второе на самом деле интереснее. cog совсем мало чего умеет, кажется, я даже не могу
парсить из него код. Только генерация. Но мне бы и для c# тоже надо.

python — препонтный препроцессор для переполнения стека

Я часто ловлю себя на мысли: «Почему препроцессор C ++ такой слабый?»

Обычный счетчик выглядит так: «С ++ вместо этого использует шаблонное метапрограммирование». Тем не менее, существует множество ситуаций, когда шаблоны не взламывают это.

Например, я сейчас пишу батут, который занимает С-struct указатель на таблицу функций (которая принадлежит библиотеке, с которой я взаимодействую) и отображает функции на методы соответствующего класса C ++. Каждый раз, когда обновление библиотеки изменяет С-структура, это собирается создать некоторый ад обслуживания. Мне нужно изменить исходный код в четырех разных местах.

И кроме обновлений, в моем коде есть огромное количество дублирования, которое просто невозможно обойти.

Что мне действительно нужно, так это код для генерации кода.

Так как насчет использования чего-то похожего на Python в качестве препроцессора?

Таким образом, сразу создается объект препроцессора, хранящий все указатели функций.

Позже мы можем использовать эту переменную:

и т. д., чтобы в приведенном выше примере были сгенерированы строки

Подобные циклы будут обрабатывать построение батутов и т. Д. И когда изменяется базовая C-структура, все остается синхронизированным.

Можно было бы пошагово пройти через препроцессор. Отладка на уровне препроцессора будет такой же простой, как и отладка кода Python.

Итак, мой вопрос: было ли предпринято что-то подобное? Если нет, есть ли веская причина, почему нет? И если так, какой был вывод?

Решение

Попробуйте написать генератор кода — он может быть специфическим для этой проблемы или более общим. Это не обязательно должен быть препроцессор, по крайней мере, начальная версия. Вы можете разработать / расширить его позже.

Генераторы кода обычно используются, в том числе такие случаи, как этот. Следует помнить одну вещь: правильные зависимости в системе сборки, чтобы сгенерированные файлы создавались заново и т. Д.

Другие решения

Вероятно, такого широко распространенного инструмента уже нет, потому что он не нужен в большинстве случаев использования, а во многих других случаях он значительно затруднит понимание и сопровождение исходного кода (планирование в будущем).

К вашему сведению такой препроцессор ( pcpp ) может быть на удивление простым:

Пример ввода ( t.cp ):

pcpp Реализация выше осторожна, чтобы сохранить новые строки, поэтому, если у вас есть ошибка в вашем коде Python, обратная трассировка будет содержать имя файла t.cp и правильный номер строки. Добавляя эмиссию #line директивы, номера строк в сообщениях об ошибках C (++) также могут быть исправлены.

(Обратите внимание, что c_escape не работает должным образом с орграфами и триграфами.)

pypreprocessor 0.7.7

pip install pypreprocessor Copy PIP instructions

Last released: Feb 12, 2020

Run c-style preprocessor directives in python modules

Navigation

Project links

Statistics

View statistics for this project via Libraries.io, or by using Google BigQuery

License: MIT License (The MIT License (MIT))

Maintainers

Classifiers

  • Development Status
    • 4 — Beta
  • Environment
    • Other Environment
  • Intended Audience
    • Developers
  • License
    • OSI Approved :: MIT License
  • Operating System
    • OS Independent
  • Programming Language
    • Python
    • Python :: 2.5
    • Python :: 2.6
    • Python :: 3
    • Python :: 3.0
    • Python :: 3.1
  • Topic
    • Software Development :: Code Generators
    • Software Development :: Libraries :: Python Modules
    • Software Development :: Pre-processors

Project description

Features

support c-style directives in python

  • using an intentionally limited subset of the c directives, and then some…

can run post-processed code on-the-fly

  • pre-process and run code all in one shot
  • accurately preserves error tracebacks for the source file

can output to a file

  • as easily as setting a flag
  • with a user specified filename if defined

can strip all pre-processor data from the output

  • as easily as setting a flag
  • removes all preprocessor directives
  • removes all preprocessor specific code

#defines can be set in code prior to processing

  • useful if you need to run a file in different modes
  • eliminates the need for decision logic in the preprocessor itself

nested #ifdef directives are supported

helpfull for more complicated code

endifall gives the opportunity to end all open blocks

Benefits

Ease of Use

  • all of the functionality required to run the pypreprocessor is contained in a single module
  • adding preprocessor support is as simple as importing pypreprocessor to a file containing preprocessor directives and telling it to run
  • the preprocessor preserves errors in the pre and post processed code as much as possible
  • the preprocessor will break execution and output traceback errors if it sees invalid preprocessor directives

Dynamic

  • pypreprocessor has multiple option of operation
  • it can generate a new post-processed version of a source file absent all of the preprocessor information
  • or it can pre-process and run code transparently the same way as c-style languages do

Simple

  • the source footprint for the pypreprocessor code is very small
  • the preprocessor is designed to be as simple and lightweight as possible

Syntax

The syntax for pypreprocessor uses a select subset of the stanard c-style preprocessor directives, and then some…

Supported directives

define non-value constants used by the preprocessor

remove a non-value constant from the list of defined constants

makes the subsequent block of code available if the specified constant is set

makes the subsequent block of code available if all of the preceding #ifdef statements return false

required to close out an #ifdef/#else block

possibility to close all open blocks

exclude the subsequent block of code (conditionals not included). I know it doesn’t fit into the standard set of c-style directives but it’s too handy to exclude (no pun).

required to close out an #exclude block

Options

The following options need to be set prior to pypreprocessor.parse()

add defines to the preprocessor programmatically, this allows the source file to have some decision logic to decide which ‘defines’ need to be set

set the options of the preprocessor:

  • run: Run the preprocessed code if true. Default is true
  • resume: Return after a file is preprocessed and can preprocess a next file if true. Default is false
  • save: Save preprocessed code if true. Default is true

required if you are preprocessing a module that is going to be imported. you can also use it to process external files.

set this to get a user defined name for the output file, otherwise the default is used: 'inputFile_out.py'

set this to remove the metadata from the output, useful if you’re generating a ‘clean’ version of the source

set this to make preprocessor use encoding

Applications

Include support for debug-specific information

It is often useful to provide statements within code specific to debugging, such as, print statements used to verify correct outputs. But, removing those debug statements can be painful and introduce new bugs while migrating to the production version.

Why not leave them…

By setting a “#define debug” at the top of the module and wrapping all of the debug statements in “#ifdef debug” blocks; disabling that code during production is as easy as adding another hash mark before the “#define debug” statement.

For a working example see:

Python2 -> Python3 code conversion

Producing code that can be run in both Python 2 and Python 3 is as easy as…

  1. check the version of the python interpreter in use
  2. set a #define in the preprocessor indicating the version
  3. add #ifdef directives on the code that is version specific

For a working example see:

Writing code from Debug -> Production in the same file

Ever wanted to run a file with debug-specific code during development but output a clean version specific source file for release? Here’s how…

  1. make a source file that takes the arguments ‘debug’ and ‘production’
  2. put the debug code in “#ifdef debug” blocks
  3. then run ‘filename.py debug’ during development
  4. or ‘filename.py production’ to output a version without any of the pypreprocessor metadata

For a working example see:

Note: This file also contains a ‘postprocessed’ mode that shows what pypreprocessor actually outputs. It’s so simple it’ll probably make you ::facepalm:: 😉

Реализация препроцессора для C на Python

support c-style directives in python

  • using an intentionally limited subset of the c directives, and then some.

can run post-processed code on-the-fly

  • pre-process and run code all in one shot
  • accurately preserves error tracebacks for the source file

can output to a file

  • as easily as setting a flag
  • with a user specified filename if defined

can strip all pre-processor data from the output

  • as easily as setting a flag
  • removes all preprocessor directives
  • removes all preprocessor specific code

#defines can be set in code prior to processing

  • useful if you need to run a file in different modes
  • eliminates the need for decision logic in the preprocessor itself

nested #ifdef directives are supported

  • helpfull for more complicated code
  • #endifall gives the opportunity to end all open blocks

Ease of Use

  • all of the functionality required to run the pypreprocessor is contained in a single module
  • adding preprocessor support is as simple as importing pypreprocessor to a file containing preprocessor directives and telling it to run
  • the preprocessor preserves errors in the pre and post processed code as much as possible
  • the preprocessor will break execution and output traceback errors if it sees invalid preprocessor directives
Мастер Йода рекомендует:  Приватность в Интернет

Dynamic

  • pypreprocessor has multiple option of operation
  • it can generate a new post-processed version of a source file absent all of the preprocessor information
  • or it can pre-process and run code transparently the same way as c-style languages do

Simple

  • the source footprint for the pypreprocessor code is very small
  • the preprocessor is designed to be as simple and lightweight as possible

The syntax for pypreprocessor uses a select subset of the stanard c-style preprocessor directives, and then some.

Supported directives

  • define non-value constants used by the preprocessor
  • remove a non-value constant from the list of defined constants
  • makes the subsequent block of code available if the specified constant is set
  • makes the subsequent block of code available if all of the preceding #ifdef statements return false
  • required to close out an #ifdef/#else block
  • possibility to close all open blocks
  • exclude the subsequent block of code (conditionals not included). I know it doesn’t fit into the standard set of c-style directives but it’s too handy to exclude (no pun).
  • required to close out an #exclude block

Options

The following options need to be set prior to pypreprocessor.parse()

add defines to the preprocessor programmatically, this allows the source file to have some decision logic to decide which ‘defines’ need to be set

set the options of the preprocessor:

  • run: Run the preprocessed code if true. Default is true
  • resume: Return after a file is preprocessed and can preprocess a next file if true. Default is false
  • save: Save preprocessed code if true. Default is true

required if you are preprocessing a module that is going to be imported. you can also use it to process external files.

set this to get a user defined name for the output file, otherwise the default is used: ‘inputFile_out.py’

set this to remove the metadata from the output, useful if you’re generating a ‘clean’ version of the source

set this to make preprocessor use encoding

Include support for debug-specific information

It is often useful to provide statements within code specific to debugging, such as, print statements used to verify correct outputs. But, removing those debug statements can be painful and introduce new bugs while migrating to the production version.

Why not leave them.

By setting a «#define debug» at the top of the module and wrapping all of the debug statements in «#ifdef debug» blocks; disabling that code during production is as easy as adding another hash mark before the «#define debug» statement.

For a working example see:

Python2 -> Python3 code conversion

Producing code that can be run in both Python 2 and Python 3 is as easy as.

  1. check the version of the python interpreter in use
  2. set a #define in the preprocessor indicating the version
  3. add #ifdef directives on the code that is version specific

For a working example see:

Writing code from Debug -> Production in the same file

Ever wanted to run a file with debug-specific code during development but output a clean version specific source file for release? Here’s how.

  1. make a source file that takes the arguments ‘debug’ and ‘production’
  2. put the debug code in «#ifdef debug» blocks
  3. then run ‘filename.py debug’ during development
  4. or ‘filename.py production’ to output a version without any of the pypreprocessor metadata

For a working example see:

Note: This file also contains a ‘postprocessed’ mode that shows what pypreprocessor actually outputs. It’s so simple it’ll probably make you ::facepalm:: 😉

Как бы вы сделать эквивалент директивы препроцессора в Python?

Есть ли способ сделать следующие директивы препроцессора в Python?

Там же __debug__ , которая представляет собой особую ценность , что компилятор делает Preprocess.

__debug__ будет заменена константой 0 или 1 компилятор, и оптимизатор удалит все if 0: строки , прежде чем ваш источник интерпретирован.

Я написал питон препроцессор под названием pypreprocessor, что делает именно то, что вы описываете.

Вот пример, чтобы сделать то, что вы описываете.

pypreprocessor способна гораздо больше, чем просто на лету предварительной обработки. Чтобы увидеть больше примеров использования случая проверить проект на Google Code.

Обновление: Более подробная информация о pypreprocessor

Как я выполнить предварительную обработку просто. Из приведенного выше примера, препроцессор импортирует объект pypreprocessor, которая создается в модуле pypreprocessor. При вызове разобраны () на препроцессоре ней самостоятельно потребляет файл, который импортируется в и генерирует временную копию себя, что комментарии из всех кода препроцессора (чтобы избежать препроцессора от вызова себя рекурсивен в бесконечном цикле) и закомментирует все неиспользованные части.

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

Затем созданный файл, содержащий постобработанный код выполняется на лету.

Достоинство использования этого метода над просто добавить кучу если заявления встраивать в коде, не будет времени выполнения впустую оценок бесполезных заявлений, потому что закомментированы части коды будут исключены из скомпилированных .pyc файлов.

Недостаток (и моя первоначальная причина для создания модуля) является то, что вы не можете запустить как питон 2x и 3x питона в том же файл, потому что питоны интерпретатор выполняет полную проверку синтаксиса перед выполнением коды и будете отвергать любые версии конкретного кода до препроцессор разрешено запускать :: вздыхать . Моя первоначальная цель состояла в том, чтобы иметь возможность разработать 2x и 3x код бок о бок в одном файле, который будет создавать версии конкретный байткод в зависимости от того, что он работает на.

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

Кроме того, если вы хотите создать версию, которая имеет все директивы препроцессора, а также любые из #ifdefs, которые исключены удалено, это так просто, как установка флага в коде препроцессора перед вызовом разобраны (). Это делает удаление нежелательного кода из версии конкретного исходного файла одностадийного процесса (против ползающих по коду и удаление, если заявления вручную).

Создание расширения C++ для Python Create a C++ extension for Python

Модули, написанные на C++ (или C), обычно используются для расширения возможностей интерпретатора Python, а также для доступа к низкоуровневым возможностям операционной системы. Modules written in C++ (or C) are commonly used to extend the capabilities of a Python interpreter as well as to enable access to low-level operating system capabilities. Существует три основных типа модулей: There are three primary types of modules:

  • Модули акселератора: так как Python — это интерпретируемый язык, для повышения производительности некоторые фрагменты кода могут быть написаны на C++. Accelerator modules: because Python is an interpreted language, certain pieces of code can be written in C++ for higher performance.
  • Модули оболочки: предоставляют существующие интерфейсы C/C++ для кода Python или адаптированный API, который удобно использовать в Python. Wrapper modules: expose existing C/C++ interfaces to Python code or expose a more «Pythonic» API that’s easy to use from Python.
  • Модули низкоуровневого системного доступа: созданы для доступа к низкоуровневым функциям среды выполнения CPython, операционной системы и базового оборудования. Low-level system access modules: created to access lower-level features of the CPython runtime, the operating system, or the underlying hardware.

В этой статье рассматривается создание модуля расширения C++ для CPython, вычисляющего гиперболический тангенс и вызывающего его из кода Python. This article walks through building a C++ extension module for CPython that computes a hyperbolic tangent and calls it from Python code. Подпрограмма реализована сначала на языке Python, чтобы продемонстрировать относительный прирост производительности по сравнению с реализацией той же подпрограммы на C++. The routine is implemented first in Python to demonstrate the relative performance gain of implementing the same routine in C++.

В этой статье также показаны два способа сделать C++ доступным для Python: This article also demonstrates two ways to make the C++ available to Python:

  • Стандартные расширения CPython, как описано в документации по Python The standard CPython extensions as described in the Python documentation
  • PyBind11 (рекомендуется для C++ 11 благодаря своей простоте). PyBind11, which is recommended for C++ 11 because of its simplicity.

Сравнение этих и других средств описано в разделе Альтернативные подходы в конце этой статьи. A comparison between these and other means is found under alternative approaches at the end of this article.

Полный пример из этого руководства: python-samples-vs-cpp-extension (GitHub). The completed sample from this walkthrough can be found on python-samples-vs-cpp-extension (GitHub).

Предварительные требования Prerequisites

Visual Studio 2020 или более поздней версии с установленными рабочими нагрузками Разработка классических приложений на C++ и Разработка на Python с параметрами по умолчанию. Visual Studio 2020 or later with both the Desktop Development with C++ and Python Development workloads installed with default options.

В рабочей нагрузке Разработка на Python также установите флажок справа для параметра Собственные средства разработки Python. In the Python Development workload, also select the box on the right for Python native development tools. Этот параметр позволяет настроить большую часть конфигурации, описанной в этой статье. This option sets up most of the configuration described in this article. (Этот параметр также автоматически включает рабочую нагрузку C++.) (This option also includes the C++ workload automatically.)

При установке рабочей нагрузки Приложения для обработки и анализа данных и аналитические приложения по умолчанию включаются среда Python и параметр Собственные средства разработки Python. Installing the Data science and analytical applications workload also includes Python and the Python native development tools option by default.

Дополнительные сведения, в том числе об использовании других версий Visual Studio, см. в руководстве по установке поддержки Python для Visual Studio. For more information, see Install Python support for Visual Studio, including using other versions of Visual Studio. Если вы устанавливаете Python отдельно, обязательно выберите параметры Download debugging symbols (Скачать отладочные символы) и Download debug binaries (Скачать двоичные файлы отладки) в разделе Дополнительные параметры установщика. If you install Python separately, be sure to select Download debugging symbols and Download debug binaries under Advanced Options in the installer. Это гарантирует наличие необходимых отладочных библиотек, если вы решите сделать отладочную сборку. This option ensures that you have the necessary debug libraries available if you choose to do a debug build.

Создание приложения Python Create the Python application

Создайте проект Python в Visual Studio, выбрав Файл > Создать > Проект. Create a new Python project in Visual Studio by selecting File > New > Project. Выполните поиск слова «Python», выберите шаблон Приложение Python, назначьте ему подходящее имя и расположение, а затем нажмите кнопку ОК. Search for «Python», select the Python Application template, give it a suitable name and location, and select OK.

Для работы с C++ необходимо использовать 32-разрядный интерпретатор Python (рекомендуемая версия — Python 3.6 или более поздняя версия). Working with C++ requires that you use a 32-bit Python interpreter (Python 3.6 or above recommended). В окне обозревателя решений в Visual Studio разверните узел проекта, а затем — узел окружения Python. In the Solution Explorer window of Visual Studio, expand the project node, then expand the Python Environments node. Если 32-разрядное окружение не указано как окружение по умолчанию (не выделено жирным шрифтом или меткой глобальное значение по умолчанию), следуйте инструкциям по выбору окружения Python для проекта. If you don’t see a 32-bit environment as the default (either in bold, or labeled with global default), then follow the instructions on Select a Python environment for a project. Если у вас не установлена 32-разрядная версия интерпретатора, см. инструкции по установке интерпретаторов Python. If you don’t have a 32-bit interpreter installed, see Install Python interpreters.

В файл .py проекта вставьте следующий код, отвечающий за тестирование производительности при вычислении гиперболического тангенса (для упрощения сравнения реализуется без использования математической библиотеки). In the project’s .py file, paste the following code that benchmarks the computation of a hyperbolic tangent (implemented without using the math library for easier comparison). Вы можете ввести код вручную, чтобы оценить некоторые функции редактирования Python. Feel free to enter the code manually to experience some of the Python editing features.

Запустите программу с помощью команды Отладка > Запуск без отладки (или нажмите клавиши CTRL+F5), чтобы просмотреть результаты. Run the program using Debug > Start without Debugging (Ctrl+F5) to see the results. Чтобы изменить длительность выполнения тестов производительности, можно настроить переменную COUNT . You can adjust the COUNT variable to change how long the benchmark takes to run. В целях этого руководства задайте значение так, чтобы тест производительности занимал приблизительно две секунды. For the purposes of this walkthrough, set the count so that the benchmark take around two seconds.

При выполнении тестов производительности всегда выбирайте Отладка > Запуск без отладки во избежание издержки при выполнении кода в отладчике Visual Studio. When running benchmarks, always use Debug > Start without Debugging to avoid the overhead incurred when running code within the Visual Studio debugger.

Создание основных проектов C++ Create the core C++ projects

Следуйте инструкциям в этом разделе, чтобы создать два идентичных проекта C++ с именами superfastcode и superfastcode2. Follow the instructions in this section to create two identical C++ projects named «superfastcode» and «superfastcode2». Позже вы используете в каждом проекте разные способы предоставления кода C++ для Python. Later you’ll use different means in each project to expose the C++ code to Python.

Убедитесь, что в переменной среды PYTHONHOME указан путь к интерпретатору Python, который вы собираетесь использовать. Make sure the PYTHONHOME environment variable is set to the Python interpreter you want to use. Проекты C++ в Visual Studio используют эту переменную для поиска различных файлов, например python.h, используемых при создании модулей расширения для Python. The C++ projects in Visual Studio rely on this variable to locate files such as python.h, which are used when creating a Python extension.

Щелкните решение в обозревателе решений правой кнопкой мыши и выберите Добавить > Новый проект. Right-click the solution in Solution Explorer and select Add > New Project. Решение Visual Studio может одновременно содержать проекты Python и C++ (одно из преимуществ использования Visual Studio для Python). A Visual Studio solution can contain both Python and C++ projects together (which is one of the advantages of using Visual Studio for Python).

Выполните поиск «C++», выберите Пустой проект, укажите имя superfastcode (superfastcode2 для второго проекта) и выберите ОК. Search on «C++», select Empty project, specify the name «superfastcode» («superfastcode2» for the second project), and select OK.

Если вы уже установили собственные средства разработки Python в Visual Studio, то можете начать с шаблона Модуль расширения Python, который уже содержит большую часть описываемых ниже функций. With the Python native development tools installed in Visual Studio, you can start with the Python Extension Module template instead, which has much of what’s described below already in place. Но в этом пошаговом руководстве начало работы с пустого проекта позволяет шаг за шагом продемонстрировать создание модуля расширения. For this walkthrough, though, starting with an empty project demonstrates building the extension module step by step. Когда вы освоите процесс, шаблон позволит вам сэкономить время при написании собственных расширений. Once you understand the process, the template saves you time when writing your own extensions.

Создайте файл C++ в новом проекте. Для этого щелкните правой кнопкой мыши узел Исходные файлы и выберите Добавить > Новый элемент, затем выберите Файл C++, присвойте ему имя module.cpp и нажмите кнопку ОК. Create a C++ file in the new project by right-clicking the Source Files node, then select Add > New Item, select C++ File, name it module.cpp , and select OK.

Файл с расширением .cpp нужен, чтобы активировать страницы свойств C++ в последующих шагах. A file with the .cpp extension is necessary to turn on the C++ property pages in the steps that follow.

В обозревателе решений щелкните проект C++ правой кнопкой мыши и выберите пункт Свойства. Right-click the C++ project in Solution Explorer, select Properties.

В верхней части появившегося диалогового окна Страницы свойств задайте для параметра Конфигурация значение Все конфигурации, а для параметра ПлатформаWin32. At the top of the Property Pages dialog that appears, set Configuration to All Configurations and Platform to Win32.

Задайте определенные свойства, как описано в таблице ниже, а затем нажмите кнопку ОК. Set the specific properties as described in the following table, then select OK.

Tab Tab Свойство. Property Значение Value
Общие сведения General Общие > Целевое имя General > Target Name Укажите имя модуля, которое будет использоваться из Python в инструкциях from. import . Specify the name of the module as you want to refer to it from Python in from. import statements. Это же имя используется в C++ при определении модуля для Python. You use this same name in the C++ when defining the module for Python. Если вы хотите применять имя проекта в качестве имени модуля, сохраните значение по умолчанию $(ProjectName). If you want to use the name of the project as the module name, leave the default value of $(ProjectName).
Общие > Целевое расширение General > Target Extension .pyd .pyd
Проект по умолчанию > Тип конфигурации Project Defaults > Configuration Type Динамическая библиотека (.dll) Dynamic Library (.dll)
C/C++ > Общие C/C++ > General Дополнительные каталоги включаемых файлов Additional Include Directories Добавьте подходящую для вашей установки папку include Python, например c:\Python36\include . Add the Python include folder as appropriate for your installation, for example, c:\Python36\include .
C/C++ > Препроцессор C/C++ > Preprocessor Определения препроцессора Preprocessor Definitions Только CPython. Добавьте Py_LIMITED_API; в начало строки (включая точку с запятой). CPython only: add Py_LIMITED_API; to the beginning of the string (including the semicolon). Это определение ограничивает некоторые функции, которые можно вызывать из Python, и расширяет возможности по переносу кода между разными версиями Python. This definition restricts some of the functions you can call from Python and makes the code more portable between different versions of Python. Если вы работаете с PyBind11, не добавляйте это определение. В противном случае вы увидите ошибки сборки. If you’re working with PyBind11, don’t add this definition, otherwise you’ll see build errors.
C/C++ > Создание кода C/C++ > Code Generation Библиотека времени выполнения Runtime Library Многопоточная DLL (/MD) (см. предупреждение ниже) Multi-threaded DLL (/MD) (see Warning below)
Компоновщик > Общие Linker > General Дополнительные каталоги библиотек Additional Library Directories Добавьте подходящую для вашей установки папку Python libs с файлами .lib, например c:\Python36\libs . Add the Python libs folder containing .lib files as appropriate for your installation, for example, c:\Python36\libs . (Обязательно укажите папку libs, содержащую файлы .lib, но не папку LIb, содержащую файлы .py.) (Be sure to point to the libs folder that contains .lib files, and not the Lib folder that contains .py files.)

Если вы не видите вкладку C/C++ в свойствах проекта, значит, в проекте нет файлов, определенных как исходные файлы C и C++. If you don’t see the C/C++ tab in the project properties, it’s because the project doesn’t contain any files that it identifies as C/C++ source files. Такая ситуация может возникнуть, если вы создали исходный файл без расширения .c или .cpp. This condition can occur if you create a source file without a .c or .cpp extension. Например, если ранее вы случайно ввели module.coo вместо module.cpp в диалоговом окне создания элемента, Visual Studio создает файл, но не задает для него тип «Код C/C+», что требуется для активации вкладки свойств C/C++. Такая неправильная идентификация остается актуальной и в случае переименования файла с использованием .cpp . For example, if you accidentally entered module.coo instead of module.cpp in the new item dialog earlier, then Visual Studio creates the file but doesn’t set the file type to «C/C+ Code,» which is what activates the C/C++ properties tab. Such misidentification remains the case even if you rename the file with .cpp . Чтобы правильно задать тип файла, в обозревателе решений щелкните файл правой кнопкой мыши, выберите Свойства, затем установите для параметра Тип файла значение Код C/C++. To set the file type properly, right-click the file in Solution Explorer, select Properties, then set File Type to C/C++ Code.

Для параметра C/C++ > Создание кода > Библиотека времени выполнения обязательно задайте значение Многопоточная библиотека DLL (/MD) даже для конфигурации отладки, так как этот параметр используется при сборке неотладочных двоичных файлов Python. Always set the C/C++ > Code Generation > Runtime Library option to Multi-threaded DLL (/MD), even for a debug configuration, because this setting is what the non-debug Python binaries are built with. Если при использовании CPython вы задали параметр Многопоточная DLL с возможностью отладки (/MDd), при сборке конфигурации Отладка возникает ошибка C1189: Py_LIMITED_API несовместим с Py_DEBUG, Py_TRACE_REFS и Py_REF_DEBUG. With CPython, if you happen to set the Multi-threaded Debug DLL (/MDd) option, building a Debug configuration produces error C1189: Py_LIMITED_API is incompatible with Py_DEBUG, Py_TRACE_REFS, and Py_REF_DEBUG. Кроме того, если удалить Py_LIMITED_API (требуется при использовании CPython, но не PyBind11) во избежание этой ошибки сборки, Python аварийно завершает работу при попытке импортировать модуль. Furthermore, if you remove Py_LIMITED_API (which is required with CPython, but not PyBind11) to avoid the build error, Python crashes when attempting to import the module. (Этот сбой возникает в вызове библиотеки DLL PyModule_Create , как описано ниже, и сопровождается сообщением Неустранимая ошибка Python: PyThreadState_Get: нет текущего потока.) (The crash happens within the DLL’s call to PyModule_Create as described later, with the output message of Fatal Python error: PyThreadState_Get: no current thread.)

Параметр /MDd используется для сборки двоичных файлов отладки Python (например, python_d.exe), но его выбор для библиотеки DLL расширения по-прежнему вызывает ошибку сборки Py_LIMITED_API . The /MDd option is used to build the Python debug binaries (such as python_d.exe), but selecting it for an extension DLL still causes the build error with Py_LIMITED_API .

Щелкните проект C++ правой кнопкой мыши и выберите Сборка, чтобы протестировать конфигурации (для отладки и выпуска). Right-click the C++ project and select Build to test your configurations (both Debug and Release). Файлы .pyd находятся в папке solution в каталогах Debug и Release, а не в самой папке проекта C++. The .pyd files are located in the solution folder under Debug and Release, not the C++ project folder itself.

Добавьте следующий код в файл module.cpp проекта: Add the following code to the C++ project’s module.cpp file:

Еще раз выполните сборку проекта C++, чтобы убедиться в правильности кода. Build the C++ project again to confirm that your code is correct.

Если вы еще этого не сделали, повторите предыдущие действия, чтобы создать второй проект с именем superfastcode2 с идентичным содержимым. If you haven’t already done so, repeat the steps above to create a second project named «superfastcode2» with identical contents.

Преобразование проекта C++ в расширения для Python Convert the C++ projects to extensions for Python

Чтобы превратить библиотеку DLL на C++ в расширение для Python, сначала нужно изменить экспортированные методы для взаимодействия с типами Python. To make the C++ DLL into an extension for Python, you first modify the exported methods to interact with Python types. После этого нужно добавить функцию, экспортирующую модуль, а также определения методов модуля. You then add a function that exports the module, along with definitions of the module’s methods.

В последующих разделах объясняется, как выполнять эти действия с помощью расширений CPython и PyBind11. The sections that follow explain how to perform these steps using both the CPython extensions and PyBind11.

Расширения CPython CPython extensions

Пояснения к тому, что показано в этом разделе для Python 3.x, см. в справочном руководстве по API Python/C и, главным образом, в разделе Module Objects (Модульные объекты) на сайте python.org (не забудьте выбрать вашу версию Python в раскрывающемся списке в правом верхнем углу, чтобы увидеть подходящую документацию). For background on what’s shown in this section for Python 3.x, refer to the Python/C API Reference Manual and especially Module Objects on python.org (remember to select your version of Python from the drop-down control on the upper right to view the correct documentation).

Если вы работаете с Python 2.7, см. статьи Extending Python 2.7 with C or C++ (Расширение Python 2.7 с помощью C и C++) и Porting Extension Modules to Python 3 (Перенос модулей расширения на Python 3) на сайте python.org. If you’re working with Python 2.7, refer instead to Extending Python 2.7 with C or C++ and Porting Extension Modules to Python 3 (python.org).

В верхнюю часть module.cpp включите Python.h: At the top of module.cpp, include Python.h:

Измените метод tanh_impl , чтобы он принимал и возвращал типы Python ( PyOjbect* ): Modify the tanh_impl method to accept and return Python types (a PyOjbect* , that is):

Добавьте структуру, определяющую способ представления функции tanh_impl C++ для Python: Add a structure that defines how the C++ tanh_impl function is presented to Python:

Добавьте структуру, которая определяет модуль так, как вы хотите ссылаться на него в своем коде Python, в частности, при использовании оператора from. import . Add a structure that defines the module as you want to refer to it in your Python code, specifically when using the from. import statement. (Сделайте это значением в свойствах проекта в разделе Свойства конфигурации > Общие > Целевое имя.) В приведенном ниже примере имя модуля superfastcode означает, что from superfastcode import fast_tanh можно использовать в Python, так как функция fast_tanh определена в superfastcode_methods . (Make this match the value in the project properties under Configuration Properties > General > Target Name.) In the following example, the «superfastcode» module name means you can use from superfastcode import fast_tanh in Python, because fast_tanh is defined within superfastcode_methods . (Внутренние для проекта C++ имена файлов, такие как module.cpp, являются несущественными.) (Filenames internal to the C++ project, like module.cpp, are inconsequential.)

Добавьте метод, вызываемый Python при загрузке модуля. Он должен иметь имя PyInit_ , где точно соответствует значению свойства Общие > Целевое имя проекта C++ (то есть соответствует имени файла .pyd, созданного проектом). Add a method that Python calls when it loads the module, which must be named PyInit_ , where exactly matches the C++ project’s General > Target Name property (that is, it matches the filename of the .pyd built by the project).

Задайте целевую конфигурацию Выпуск и повторно создайте проект C++, чтобы проверить код. Set the target configuration to Release and build the C++ project again to verify your code. Если возникли ошибки, обратитесь к разделу Устранение проблем, расположенному ниже. If you encounter errors, see the Troubleshooting section below.

PyBind11 PyBind11

Если вы выполнили действия, описанные в предыдущем разделе, вы наверняка заметили, что использовали много стандартного кода для создания необходимых структур модуля для кода C++. If you completed the steps in the previous section, you certainly noticed that you used lots of boilerplate code to create the necessary module structures for the C++ code. PyBind11 упрощает процесс с помощью макросов в файле заголовка C++, которые достигают того же результата с гораздо меньшим объемом кода. PyBind11 simplifies the process through macros in a C++ header file that accomplish the same result with much less code. Сведения о концепциях в этом разделе см. в разделе Основы PyBind11 (github.com). For background on what’s shown in this section, see PyBind11 basics (github.com).

Установите PyBind11 с помощью pip: pip install pybind11 или py -m pip install pybind11 . Install PyBind11 using pip: pip install pybind11 or py -m pip install pybind11 .

В верхнюю часть module.cpp включите pybind11.h: At the top of module.cpp, include pybind11.h:

В нижней части module.cpp используйте макрос PYBIND11_MODULE , чтобы определить точку входа в функцию C++: At the bottom of module.cpp, use the PYBIND11_MODULE macro to define the entrypoint to the C++ function:

Задайте целевую конфигурацию Выпуск и создайте проект C++, чтобы проверить код. Set the target configuration to Release and build the C++ project to verify your code. Если возникли ошибки, читайте следующий раздел об устранении неполадок. If you encounter errors, see the next section on troubleshooting.

Устранение неполадок Troubleshooting

Модуль C++ может не компилироваться по следующим причинам: The C++ module may fail to compile for the following reasons:

Не удалось найти Python.h (E1696: не удается открыть исходный файл Python.h и (или) C1083: не удается открыть включаемый файл: Python.h: отсутствует такой файл или каталог): убедитесь, что путь в разделе C/C++ > Общие > Дополнительные каталоги включаемых файлов в свойствах проекта указывает на папку include установки Python. Unable to locate Python.h (E1696: cannot open source file «Python.h» and/or C1083: Cannot open include file: «Python.h»: No such file or directory): verify that the path in C/C++ > General > Additional Include Directories in the project properties points to your Python installation’s include folder. См. шаг 6 в разделе Создание основного проекта C++. See step 6 under Create the core C++ project.

Не удается найти библиотеки Python. Убедитесь, что путь Компоновщик > Общие > Дополнительные каталоги библиотек в свойствах проекта указывает на папку libs установки Python. Unable to locate Python libraries: verify that the path in Linker > General > Additional Library Directories in the project properties points to your Python installation’s libs folder. См. шаг 6 в разделе Создание основного проекта C++. See step 6 under Create the core C++ project.

Ошибки компоновщика, связанные с целевой архитектурой. Измените архитектуру целевого проекта C++ в соответствии с вашей установкой Python. Linker errors related to target architecture: change the C++ target’s project architecture to match that of your Python installation. Например, если вы хотите использовать в проекте C++ версию Python x64, но у вас установлена версия x86, измените проект C++ для работы с версией х86. For example, if you’re targeting x64 with the C++ project but your Python installation is x86, change the C++ project to target x86.

Тестирование кода и сравнение результатов Test the code and compare the results

Теперь, когда библиотека DLL структурирована как расширения Python, можно ссылаться на них из проекта Python, импортировать модули и использовать их методы. Now that you have the DLLs structured as Python extensions, you can refer to them from the Python project, import the modules, and use their methods.

Предоставление доступа к библиотеке DLL для Python Make the DLL available to Python

Предоставить Python доступ к библиотеке DLL можно двумя способами. There are two ways to make the DLL available to Python.

Первый метод работает, если проект Python и проект C++ находятся в одном решении. The first method works if the Python project and the C++ project are in the same solution. В обозревателе решений щелкните правой кнопкой мыши узел Ссылки в проекте Python и затем выберите команду Добавить ссылку. Go to Solution Explorer, right-click the References node in your Python project, and then select Add Reference. В открывшемся диалоговом окне перейдите на вкладку Проекты, выберите проекты superfastcode и superfastcode2 и нажмите кнопку OK. In the dialog that appears, select the Projects tab, select both the superfastcode and superfastcode2 projects, and then select OK.

Альтернативный метод, описанный далее, предполагает установку модуля в глобальной среде Python и предоставление к нему доступа в других проектах Python. The alternate method, described in the following steps, installs the module in the global Python environment, making it available to other Python projects as well. (При этом обычно требуется обновить базу данных завершения IntelliSense для этой среды в Visual Studio 2020 версии 15.5 и более ранних версий. (Doing so typically requires that you refresh the IntelliSense completion database for that environment in Visual Studio 2020 version 15.5 and earlier. Обновление также требуется и при извлечении модуля из среды.) Refreshing is also necessary when removing the module from the environment.)

Если вы используете Visual Studio 2020 или более поздней версии, запустите Visual Studio Installer, выберите Изменить, затем Отдельные компоненты > Компиляторы, средства сборки и среды выполнения > Набор инструментов Visual C++ 2015.3 версии 140. If you’re using Visual Studio 2020 or later, run the Visual Studio installer, select Modify, select Individual Components > Compilers, build tools, and runtimes > Visual C++ 2015.3 v140 toolset. Этот шаг необходим, так как Python (для Windows) сам создан с помощью Visual Studio 2015 (версия 14.0) и поэтому ожидает наличия этих средств при создании расширения одним из описанных здесь способов. This step is necessary because Python (for Windows) is itself built with Visual Studio 2015 (version 14.0) and expects that those tools are available when building an extension through the method described here. (Обратите внимание, что может потребоваться установить 32-разрядную версию Python и нацелить библиотеку DLL на Win32, а не на x64.) (Note that you may need to install a 32-bit version of Python and target the DLL to Win32 and not x64.)

Создайте в проекте C++ файл с именем setup.py, щелкнув проект правой кнопкой мыши и выбрав пункт Добавить > Новый элемент. Create a file named setup.py in the C++ project by right-clicking the project and selecting Add > New Item. Выберите тип файла Файл C++ (.cpp), присвойте файлу имя setup.py и щелкните ОК. (Присвоение файлу расширения .py позволяет Visual Studio распознавать его как файл Python несмотря на использование шаблона файла C++.) Then select C++ File (.cpp), name the file setup.py , and select OK (naming the file with the .py extension makes Visual Studio recognize it as Python despite using the C++ file template). Когда файл откроется в редакторе, вставьте в него следующий код в зависимости от метода расширения: When the file appears in the editor, paste the following code into it as appropriate to the extension method:

Расширения CPython (проект superfastcode): CPython extensions (superfastcode project):

Документация к этому скрипту приведена в статье о создании расширений на C и C++ (python.org). See Building C and C++ extensions (python.org) for documentation on this script.

PyBind11 (проект superfastcode2): PyBind11 (superfastcode2 project):

При использовании в командной строке код setup.py предписывает Python создать расширение с помощью набора инструментов C++ Visual Studio 2015. The setup.py code instructs Python to build the extension using the Visual Studio 2015 C++ toolset when used from the command line. Откройте командную строку с повышенными привилегиями, перейдите в папку с проектом C++ (то есть в папку, содержащую setup.py), а затем введите следующую команду: Open an elevated command prompt, navigate to the folder containing the C++ project (that is, the folder that contains setup.py), and enter the following command:

Вызов библиотеки DLL из Python Call the DLL from Python

После того как вы предоставили библиотеки DLL для Python, как описано в предыдущем разделе, вы можете вызвать функции superfastcode.fast_tanh и superfastcode2.fast_tanh2 из кода Python и сравнить их эффективность с реализацией Python: After you’ve made the DLL available to Python as described in the previous section, you can now call the superfastcode.fast_tanh and superfastcode2.fast_tanh2 functions from Python code and compare their performance to the Python implementation:

Добавьте приведенные ниже строки в файл .py, чтобы вызвать методы, экспортированные из библиотек DLL, и отобразить их выходные данные: Add the following lines in your .py file to call methods exported from the DLLs and display their outputs:

Запустите программу Python (Отладка > Запуск без отладки или CTRL+F5) и убедитесь, что подпрограмма C++ выполняется примерно в 5–20 раз быстрее, чем реализация Python. Run the Python program (Debug > Start without Debugging or Ctrl+F5) and observe that the C++ routines run approximately five to twenty times faster than the Python implementation. Обычно выводится следующий результат. Typical output appears as follows:

Если команда Запуск без отладки недоступна, в обозревателе решений щелкните правой кнопкой мыши проект Python и выберите команду Назначить запускаемым проектом. If the Start Without Debugging command is disabled, right-click the Python project in Solution Explorer and select Set as Startup Project.

Попробуйте увеличить значение переменной COUNT , чтобы разница стала еще очевиднее. Try increasing the COUNT variable so that the differences are more pronounced. Кроме того, отладочная сборка модуля C++ выполняется медленнее, чем сборка выпуска, так как отладочная сборка менее оптимизирована и включает в себя разные проверки ошибок. A Debug build of the C++ module also runs slower than a Release build because the Debug build is less optimized and contains various error checks. Вы можете свободно переключаться между этими конфигурациями для их сравнения. Feel free to switch between those configurations for comparison.

В выходных данных вы увидите, что расширение PyBind11 не такое быстрое, как расширение CPython, хотя все равно значительно быстрее, чем прямая реализация на языке Python. In the output, you can see that the PyBind11 extension isn’t as fast as the CPython extension, though it’s still significantly faster than the straight Python implementation. Разница связана с небольшим количеством издержек для отдельных вызовов, которые использует PyBind11, чтобы значительно упростить интерфейс C++. The difference is due to a small amount of per-call overhead that PyBind11 introduces in order to make its C++ interface dramatically simpler. Это различие для одного вызова почти незаметно, но так как тестовый код вызывает функции расширения 500 000 раз, издержки накапливаются. This per-call difference is actually quite negligible: because the test code calls the extension functions 500,000 times, the results you see here greatly amplify that overhead! Как правило, функция C++ выполняет гораздо больше работы, чем обычные методы fast_tanh[2] , используемые здесь, в этом случае издержки не имеют значения. Typically, a C++ function does much more work than the trivial fast_tanh[2] methods used here, in which case the overhead is unimportant. Но если вы реализуете методы, которые могут вызываться тысячи раз в секунду, подход CPython будет иметь лучшую производительность по сравнению с PyBind11. However, if you’re implementing methods that might be called thousands of times per second, using the CPython approach can result in better performance than PyBind11.

Отладка кода C++ Debug the C++ code

Visual Studio поддерживает совместную отладку кода на Python и C++. Visual Studio supports debugging Python and C++ code together. В этом разделе этот процесс рассматривается на примере проекта superfastcode. Для проекта superfastcode2 действия будут аналогичными. This section walks through the process using the superfastcode project; the steps are the same for the superfastcode2 project.

Щелкните проект Python правой кнопкой мыши в обозревателе решений, выберите Свойства, откройте вкладку Отладка и выберите Отладка > Разрешить отладку машинного кода. Right-click the Python project in Solution Explorer, select Properties, select the Debug tab, and then select the Debug > Enable native code debugging option.

При включении отладки машинного кода окно вывода Python может исчезнуть сразу после завершения программы без обычной паузы с сообщением Для продолжения нажмите любую клавишу. When you enable native code debugging, the Python output window may disappear immediately when the program has completed without giving you the usual Press any key to continue pause. Чтобы сделать паузу принудительно, добавьте параметр -i в поле Запуск > Аргументы интерпретатора на вкладке Отладка при включении отладки машинного кода. To force a pause, add the -i option to the Run > Interpreter Arguments field on the Debug tab when you enable native code debugging. Этот аргумент переводит интерпретатор Python в интерактивный режим по завершении кода, после чего он ожидает нажатия клавиш CTRL+Z > ВВОД для выхода. This argument puts the Python interpreter into interactive mode after the code finishes, at which point it waits for you to press Ctrl+Z > Enter to exit. (Кроме того, если вы не против изменения кода Python, можно добавить в конец программы операторы import os и os.system(«pause») . (Alternately, if you don’t mind modifying your Python code, you can add import os and os.system(«pause») statements at the end of your program. Этот код дублирует исходный запрос с паузой.) This code duplicates the original pause prompt.)

Чтобы сохранить изменения свойств, выберите пункт меню Файл > Сохранить. Select File > Save to save the property changes.

На панели инструментов Visual Studio измените конфигурацию сборки на режим Отладка. Set the build configuration to Debug in the Visual Studio toolbar.

Так как в отладчике код, как правило, выполняется дольше, может потребоваться уменьшить значение переменной COUNT в файле .py приблизительно в пять раз (например, вместо 500000 укажите 100000 ). Because code generally takes longer to run in the debugger, you may want to change the COUNT variable in your .py file to a value that’s about five times smaller (for example, change it from 500000 to 100000 ).

В коде C++ установите точку останова в первой строке метода tanh_impl , а затем запустите отладчик (F5 или Отладка > Начать отладку). In your C++ code, set a breakpoint on the first line of the tanh_impl method, then start the debugger (F5 or Debug > Start Debugging). При вызове этого кода отладчик останавливается. The debugger stops when that code is called. Если точка останова не сработает, проверьте, выбрана ли конфигурация отладки и сохранен ли проект (при запуске отладчика он не сохраняется автоматически). If the breakpoint is not hit, check that the configuration is set to Debug and that you’ve saved the project (which does not happen automatically when starting the debugger).

На этом этапе вы можете пошагово выполнять код на C++, проверять переменные и т. д. At this point you can step through the C++ code, examine variables, and so on. Эти возможности подробно описаны в руководстве по совместной отладке C++ и Python. These features are detailed in Debug C++ and Python together.

Альтернативные подходы Alternative approaches

Существуют различные средства для создания расширений Python, как описано в таблице ниже. There are a variety of means to create Python extensions as described in the following table. Первые две записи для CPython и PyBind11 уже обсуждались в этой статье. The first two entries for CPython and PyBind11 are what has been discussed in this article already.

Подход Approach Появление Vintage Представители Representative user(s) Преимущества Pro(s) Недостатки Con(s)
Модули расширений C/C++ для CPython C/C++ extension modules for CPython 1991 1991 Стандартная библиотека Standard Library Подробная документация и учебники. Extensive documentation and tutorials. Полный контроль. Total control. Компиляция, переносимость, управление ссылками. Compilation, portability, reference management. Хорошее знание C. High C knowledge.
PyBind11 (рекомендуется для C++) PyBind11 (Recommended for C++) 2015 2015 Упрощенная библиотека заголовков для создания привязок Python к существующему коду C++. Lightweight, header-only library for creating Python bindings of existing C++ code. Малое число зависимостей. Few dependencies. Совместимость с PyPy. PyPy compatibility. Новый, менее проверенный подход. Newer, less mature. Частое использование функций C++11. Heavy use of C++11 features. Краткий список поддерживаемых компиляторов (включает Visual Studio). Short list of supported compilers (Visual Studio is included).
Cython (рекомендуется для C) Cython (Recommended for C) 2007 2007 gevent, kivy gevent, kivy Аналогичен Python. Python-like. Высокая степень зрелости. Highly mature. Высокая производительность. High performance. Компиляция, новый синтаксис, новая цепочка инструментов. Compilation, new syntax, new toolchain.
Boost.Python Boost.Python 2002 2002 Работает практически с любым компилятором C++. Works with just about every C++ compiler. Большой и сложный набор библиотек. Содержит много обходных путей для старых компиляторов. Large and complex suite of libraries; contains many workarounds for old compilers.
ctypes ctypes 2003 2003 oscrypto oscrypto Отсутствие компиляции, широкая доступность. No compilation, wide availability. Обращение к структурам C и их изменение затруднено и подвержено ошибкам. Accessing and mutating C structures cumbersome and error prone.
SWIG SWIG 1996 1996 crfsuite crfsuite Создание привязок сразу для нескольких языков. Generate bindings for many languages at once. Чрезмерные затраты, когда единственной целью является Python. Excessive overhead if Python is the only target.
cffi cffi 2013 2013 cryptography, pypy cryptography, pypy Простота интеграции, совместимость с PyPy. Ease of integration, PyPy compatibility. Новый, менее проверенный подход. Newer, less mature.
cppyy cppyy 2020 2020 Подобен cffi при использовании C++. Similar to cffi using C++. Новый, возможны проблемы с VS 2020. Newer, may have some issues with VS 2020.

См. также See also

Полный пример из этого руководства: python-samples-vs-cpp-extension (GitHub). The completed sample from this walkthrough can be found on python-samples-vs-cpp-extension (GitHub).

Реализация предварительного процессора C в Python или JavaScript?

Есть ли известная реализация препроцессорного инструмента C, реализованного либо в Python, либо в JavaScript? Я ищу способ обеспечить предварительный процесс исходного кода C (и C) и хочу иметь возможность обрабатывать, например, условную компиляцию и макросы без, вызывающих внешний инструмент CPP или собственную библиотеку кода. Другим потенциальным вариантом использования является предварительная обработка в веб-приложении в веб-браузере.

До сих пор, я нашел в реализации Java, Perl, и, конечно же, C и C снова. Возможно, было бы правдоподобно использовать один из компиляторов C to JavaScript.

Инструменты PLY (Python Lex и Yacc) включают в себя: cpp, реализованный в Python.

Создан 03 дек. 10 2010-12-03 23:12:56 grrussel

«Внешний инструмент CPP» обычно строится прямо в компиляторе. Для чего вам это нужно? – Karl Knechtel 03 дек. 10 2010-12-03 23:15:52

Кажется, что много работы не так много. Почему это важно? – Ira Baxter 03 дек. 10 2010-12-03 23:16:11

Вы ** можете ** всегда выполнять это самостоятельно — препроцессор чрезвычайно прост (на самом деле, я считаю, что он является регулярным, поэтому вам даже не нужны контекстные свободные грамматики, чтобы интерпретировать его). – Billy ONeal 03 дек. 10 2010-12-03 23:16:46

@billy Препроцессор не прост, как только считаются макросы: затем поддерживаются сращивание строк, разделение комментариев, прагмы, конкатенация маркера и арифметика. @ira Выполнять анализ/обработку исходного кода в контекстах, где вызов внешнего инструмента невозможно, например. в браузере или в чистом питоне. @karl Мне нужен предварительный процессор, независимый от данного компилятора, поскольку я на самом деле не пытаюсь скомпилировать, а скорее код процесса, когда компилятор недоступен. – grrussel 03 дек. 10 2010-12-03 23:25:45

Любая конкретная причина, по которой вам требуется python или javascript? Если вы решите реализовать его самостоятельно, я бы рекомендовал использовать библиотеку [parsing expression grammar] (https://en.wikipedia.org/wiki/Parsing_expression_grammar), такую ​​как [treetop] (https://treetop.rubyforge.org/) для рубина. – david4dev 03 дек. 10 2010-12-03 23:42:31

В настоящее время я использую инструменты анализатора python (PLY) и хочу интегрировать предварительную обработку.JS будет работать в веб-интерфейсе в браузере. – grrussel 04 дек. 10 2010-12-04 00:28:44

@Billy: Мне потребовалось чуть больше месяца усилий для реализации большей части препроцессора на C++ (я пропустил некоторые вещи, такие как многоканальные литералы и UCN). Все сказали, что это было более 4000 строк очень сжатого C++, не включая тестовый набор. Замена макроса чрезвычайно сложна. На минимальном уровне вам нужен лексер, условный стек, макрозамена, файловый менеджер, парсер выражений, оценщик выражений и ведро меньших компонентов. Вы также должны учитывать C90, C99, C++ 03 и C++ 0x. – James McNellis 04 дек. 10 2010-12-04 00:53:51

@ Билли: Я второй Джеймс. У нас есть препроцессор, реализованный для статического анализа и инструментов преобразования исходного кода для C и C++; наш — это 10K SLOC, но также позволяет нам расширять/не расширять произвольные части. Это позволяет расширить только «плохие» условные выражения или макросы, сохранив структурированные, что позволяет нам анализировать код в основном с помощью директив препроцессора. Если вы отбросите эту способность, я уверен, что наш будет примерно того же размера, что и Джеймс ». И его свернутый код. Это просто не тривиально. – Ira Baxter 04 дек. 10 2010-12-04 05:01:25

@grrussel: Если вы используете Python, тогда вызов препроцессора не должен быть проблемой; Я предполагаю, что он может выложить. Есть мир, в котором он не может? Я не думаю, что создание инструмента статического анализа в Javascript — хорошая идея; Программы C/C++ оказываются довольно большими, когда вы обрабатываете файлы include, которые они ссылаются, и вам нужна достойная производительность в части предварительного процесса/анализа, а также в части анализа. – Ira Baxter 04 дек. 10 2010-12-04 05:05:37

@ira Производительность статического анализа, вероятно, в значительной степени зависит от общего размера программы: меня интересует анализ кода OpenCL, который по существу является расширениями C99 +, но с типичным размером программы от 10 до 100 строк; программа 1000 строк очень нетипична. Однако они часто используют функции препроцессора. Что касается того, когда не выходить из строя, я пытаюсь разделить анализ между веб-интерфейсом и веб-сервисом в python, где источник программы не доверен; Я не уверен, есть ли больше проблем, чем #include/etc/passwd при использовании CPP напрямую, но предпочитает делать предварительную обработку на стороне клиента. – grrussel 04 дек. 10 2010-12-04 07:45:52

@grrussel: Так что, возможно, я заберу свое замечание о реализации препроцессора. Я предполагаю, что большинство программ OpenCL не используют темные углы препроцессора; если вы верите в это, вы можете реализовать только основы (еще немного работы) и только оскорбить очень небольшую часть своей аудитории. Но я по-прежнему не рекомендую это делать так. Ваша программа . – Ira Baxter 04 дек. 10 2010-12-04 16:13:44

@grrussel . у архитектуры есть пользовательский интерфейс и веб-сервис; обычной практикой является размещение бизнес-логики («BL») (например, ничего, кроме презентации) на сервере, поскольку в противном случае вы обнаружите, что BL в пользовательском интерфейсе в конечном итоге будет иметь сложные взаимодействия с BL на сервере. Например, имена макросов могут когда-нибудь стать важными для вашего linter; теперь пользовательский интерфейс, делающий расширение макросов и заставляя их исчезать, должен будет передать их на сервер. – Ira Baxter 04 дек. 10 2010-12-04 16:15:51

@ira Действительно, web ui — это просто один из способов взаимодействия с анализом (библиотека, принимающая предварительно обработанный источник): для локального использования использование локального препроцессора в командной строке небезопасно, а не (очень) проблематично, предполагая, что препроцессор не нарушает несколько особых случаев с открытым контентом OpenCL. – grrussel 04 дек. 10 2010-12-04 17:19:40

5 ответов

Отъезд PLY, реализация Lex и Yacc на Python, которая содержит cpp tool implemented in python с использованием PLY lexer. Существует также pycparser, парсер, построенный поверх PLY для языка C.

Создан 28 янв. 11 2011-01-28 22:20:02 vz0

К сожалению, pycparser не поддерживает код C, за исключением того, что на него был вызван препроцессор. – grrussel 29 янв. 11 2011-01-29 08:54:38

Если ANTLR может использоваться с Python/JavaScript (я очень сомневаюсь, что последний), вы можете получить грамматику с сайта ANTLR: https://www.antlr.org/grammar/1166665121622/Cpp.tar

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

Создан 04 дек. 10 2010-12-04 00:48:13 ThiefMaster

К сожалению, сложная часть реализации препроцессора — это настраиваемая логика, которая должна быть написана для замены макросов и оценки выражения с ограниченным диапазоном. – James McNellis 04 дек. 10 2010-12-04 00:56:55

Возможно, вы создали привязку python вокруг препроцессора clang c.

Создан 04 дек. 10 2010-12-04 19:07:04 dan_waterworth

В настоящее время libclang поставляется с привязками Python. См. [Разбор C++ в Python с Clang] (https://eli.thegreenplace.net/2011/07/03/parsing-c-in-python-with-clang/). – prideout 26 июл. 15 2015-07-26 05:04:48

Система построения Firefox делает что-то подобное для своего хрома. Исходный файл с именем jar.mn содержит список всех необходимых файлов XUL/JS, и, при необходимости, эти файлы предварительно обрабатываются до их отправки. Таким образом, скрипт, который ships файлов также вызывает mini- preprocessor.

Создан 29 янв. 11 2011-01-29 17:33:03 Neil

Он также может быть найден на PYPI (Python Package Index), поэтому он может быть загружен непосредственно с помощью пип.

Если вы хотите использовать инструкции #define и #ifdef для простой условной компиляции, они должны соответствовать вашим потребностям. Вы можете запустить его как отдельный сценарий и указать входной файл (который может быть на любом языке) для обработки, или вы можете импортировать его как модуль python и использовать его для самостоятельного потребления источника python.

pypreprocessor делает не поддержка:

  • макро замена
  • логические операции (т. Е || & &)
  • математические операции (т. Е + — * /)
  • многострочный препроцессор Директивные указания

Это инструмент, разработанный специально для условной компиляции, а не метапрограммирования.

Вот пример того, как обработать файл C использованием pypreprocessor в качестве отдельного сценария:

Реализация довольно проста.

defines.append (‘отладки’):

имеет тот же эффект, добавив директиву ‘#define’ отладки в файл. Это просто удобный способ установки определений.

pypreprocessor.input = ‘input_file.c’:

Устанавливает файл, который вы хотите обработать.

pypreprocessor.output = ‘output_file.c’:

Устанавливает, где файл выводится.

pypreprocessor.removeMeta = True:

Удаляет все директивы препроцессора, найденных в файле. Сюда входят все #define, #ifdef, #undefine, #else и т. Д. Из исходного файла. Это полезно для создания «чистой» производственной версии источника минус все условные коды и директивы, которые не используются. Этот шаг также полностью необязателен. Если вы не добавите этот код, код, который не попадает в определенное условие, будет автоматически прокомментирован.

Если вам нужна замена макроса, например, источник изменений с одного языка на другой pyprocessor не поддерживает это пока. Теоретически, это не займет много работы, но лексер должен быть продлен первым, чтобы пропустить ключевые слова, которые появляются в текстовых строках.

SideNote: Он также совместим как с python2x, так и с python3k.

Отказ от ответственности: Я являюсь автором pypreprocessor.

Создан 16 мар. 11 2011-03-16 02:04:56 Evan Plaice

Добавить комментарий