Как использовать объект обернутого класса в качестве аргумента в обернутой функции - PullRequest
2 голосов
/ 23 сентября 2019

Я использую Cython, чтобы обернуть код C ++ и сделать его доступным в Python.Проблема, с которой я сталкиваюсь, заключается в том, что я хочу использовать обернутый класс в качестве аргумента в функции, которую я также хочу обернуть.Поэтому с точки зрения Python я хочу создать и изменить объект обернутого класса и использовать его в качестве аргумента для обернутой функции, которую я также хочу вызвать из Python.Надеемся, что приведенный ниже код продемонстрирует это.

Ниже вы можете найти минимальный пример в C ++, который я хочу обернуть:

. / CppCode / Settings / Settings.h

class Settings
{
public:
  Settings();
  void doSomething();
};

. / CppCode / Helper / Helper.h

#include "../Settings/Settings.h"

void myFunction(Settings settings);

Функциональность не очень важна.Поэтому я пропустил файлы .cpp.Ниже приведен мой подход к Cython:

. / CythonCode / Settings / Settings.pxd

cdef extern from "../../cppCode/Settings/Settings.h":
  cdef cppclass Settings:
    Settings() except +
    void doSomething()

. / CythonCode / Settings / Settings.pyx

# distutils: sources = ../../cppCode/Settings/Settings.cpp
# distutils: language = c++

from Settings cimport Settings

cdef class PySettings:
  cdef Settings c_settings

  def __cinit__(self):
    self.c_settings = Settings()

  def doSomething(self):
    self.c_settings.doSomething()

. / cythonCode / Helper.pxd

from Settings.Settings cimport Settings

cdef extern from "../../cppCode/Helper/Helper.h":
  void myFunction(Settings settings)

. / cythonCode / Helper.pyx

# distutils: sources = ../../cppCode/Helper/Helper.cpp
# distutils: language = c++

from Helper cimport myFunction

cdef PyMyFunction(PySettings settings):
  myFunction(settings)

run.py

import cythonCode.Settings.Settings as Settings
#import cythonCode.Helper as Helper

mySettings = Settings.PySettings()
mySettings.doSomething()

#Helper.myFunction(mySettings) # not working

Я надеюсь, что структурапроект понятен.Я бы на самом деле хотел бы иметь «Helper.pyx» и «Helper.pxd» в папке «Helper», но тогда я не знаю, как импортировать настройки.Если вы можете помочь мне исправить это, это также будет высоко оценено.Тем не менее, основная проблема заключается в том, чтобы заставить Helper работать так, чтобы я мог использовать его в «run.py».Файл «setup.py» для сборки модулей Cython выглядит просто так:

from distutils.core import setup
from Cython.Build import cythonize
from setuptools.extension import Extension

extensions = [
  Extension("Helper", ["Helper.pyx"])
]

setup(ext_modules=cythonize(extensions))

Я делаю то же самое отдельно в папке «Настройки».

Буду очень признателен за помощь в решенииэта проблема!

РЕДАКТИРОВАТЬ : Как уже упоминалось в комментариях, есть две ошибки:

1) Это должен быть Helper.PyMyFunction (mySettings) в run.py.

2) Это должно быть def вместо cdef перед PyMyFunction, потому что я определенно хочу вызвать эту функцию из Python.

EDIT2 : я играл с вашими входамии нашел неумелое решение, которое приводит к другому вопросу.Ниже приведен код, который работает для меня:

Settings.pxd

cdef extern from "../../cppCode/Settings/Settings.h":
  cdef cppclass Settings:
    Settings() except +
    void doSomething()

cdef extern from "../../cppCode/Helper/Helper.h":
  void myFunction(Settings settings)

Settings.pyx

# distutils: sources = [../../cppCode/Settings/Settings.cpp, ../../cppCode/Helper/Helper.cpp]
# distutils: language = c++

from Settings cimport Settings, myFunction

cdef class PySettings:
  cdef Settings c_settings

  def __cinit__(self):
    self.c_settings = Settings()

  def doSomething(self):
    self.c_settings.doSomething()

def PyMyFunction(PySettings settings):
  myFunction(settings.c_settings)

Когда я цитирую Settings.pyx, я могу запуститьследующий код Python, и все работает нормально:

import Settings

mySettings = Settings.PySettings()
mySettings.doSomething()

Settings.PyMyFunction(mySettings)

Что мне не нравится в этом, так это то, что обе части (Настройки и myFunction) включены в один файл.Я понятия не имею, как заставить это работать, когда эти две части находятся в отдельных файлах.

EDIT3 : Чтобы решить проблему "две части в отдельных файлах", представьте следующий код:

Settings.pxd

cdef extern from "../../cppCode/Settings/Settings.h":
  cdef cppclass Settings:
    Settings() except +
    void doSomething()

Settings.pyx

# distutils: sources = ../../cppCode/Settings/Settings.cpp
# distutils: language = c++

from Settings cimport Settings

cdef class PySettings:
  cdef Settings c_settings

  def __cinit__(self):
    self.c_settings = Settings()

  def doSomething(self):
    self.c_settings.doSomething()

Helper.pxd

from Settings cimport Settings

cdef extern from "../../cppCode/Helper/Helper.h":
  void myFunction(Settings settings)

Helper.pyx

# distutils: sources = ../../cppCode/Helper/Helper.cpp
# distutils: language = c++

from Helper cimport myFunction

def PyMyFunction(PySettings settings):
  myFunction(settings.c_settings)

Это тот же код, что и в EDIT2, но разделенный на два файла.В Helper.pxd есть только одна дополнительная строка, которая находится «из настроек cimport Settings».Тем не менее, в Helper.pyx я получаю эту ошибку:

def PyMyFunction(PySettings settings):
                ^
---------------------------------------
Helper.pyx:6:17: 'PySettings' is not a type identifier

Я пытался "из настроек Cimport PySettings", но это не работает.Продолжает происходить одна и та же ошибка.Все файлы находятся в одном каталоге.

1 Ответ

0 голосов
/ 23 сентября 2019

Ясно, что этот вопрос - просто ряд небольших проблем, с которыми что-то было довольно близко к работе:

  1. Основная первоначальная проблема заключалась в том, что функции [должны быть def или cpdef, но не cdef для вызова из Python ( Импорт функции cython: AttributeError: у объекта 'module' нет атрибута 'fun' ).Стоит помнить, что Cython компилирует / ускоряет все функции, и единственным преимуществом cdef или cpdef является то, что их можно вызывать немного быстрее из Cython.Поэтому вы должны объявить функции cdef по умолчанию.

  2. В нескольких случаях вы использовали имя типа C ++ Settings вместо класса Cython PySettings.Я предполагаю, что это была в основном опечатка.

  3. Нет необходимости делать cimport Settings в Settings.pyx и cimport Helper в Helper.pyx - Cython автоматически делает эквивалент from filename cimport *где существует файл .pxd с соответствующим именем файла.

  4. Ваша последняя проблема заключается в разделении ваших классов на несколько файлов - вы не можете cimport PySettings в Helper.pyx.Следует помнить, что «cimport» смотрит на файл .pxd - он выглядит немного как заголовок C / C ++.Если вы хотите cimport PySettings, тогда (объявление) PySettings должно быть в Settings.pxd

    cdef class PySettings:
       cdef Settings c_settings
       # signatures for any cdef functions also go here
    

    В Settings.pyx вы делаете

    cdef class PySettings:
        # don't duplicate "cdef Settings c_settings"
        # do put all the def functions here
        def __cinit__(self):
           self.c_settings = Settings()
    
        def doSomething(self):
           self.c_settings.doSomething()
    

    от этого помощника..pyx будет знать о PySettings и о том, что он имеет атрибут c_settings.

...