Может ли класс Python наследоваться от класса C++, обёрнутого с помощью SWIG?

Вопросы и ответы

Я использую версию SWIG 4.0.2 в дистрибутиве Ubuntu Windows Subsystem for Linux (WSL). Я могу обернуть C++ класс (EventProcessor), создать экземпляр этого класса в Python и передать этот экземпляр в обернутую глобальную C++ функцию (registerEventProcessor()). Но могу ли я определить класс в Python, который наследуется от базового класса EventProcessor?

Простой C++ класс и глобальная функция для обертки представлены следующим заголовочным файлом.

example4.h
#pragma once

class EventProcessor
{
private:
  int m_value;

public:
  EventProcessor( void )
  : m_value( 42 )
  {
  }

  int getValue( void ) const
  {
    return m_value;
  }

  void setValue( int value )
  {
    m_value = value;
  }
};

void registerEventProcessor( int packetId, EventProcessor * pProc )
{
  if ( pProc )
  {
    // Что-то простое, чтобы продемонстрировать, что функция успешна:
    // Сохраните данный идентификатор пакета.
    pProc->setValue( packetId );
  }
}

Файл интерфейса SWIG, который я использую для обертки C++ кода, следующий.

example4.swg
%module example4

%{
  #define SWIG_FILE_WITH_INIT

  #include "example4.h"
%}

%include "example4.h"

Командные строки, которые я использую для генерации кода обертки C++ и затем для сборки модуля расширения Python, следующие.

finch@laptop:~/work/swig_example$ swig -c++ -python example4.swg
finch@laptop:~/work/swig_example$ python3 example4_setup.py build_ext --inplace
running build_ext
building '_example4' extension
... много вывода, без ошибок ...

Использование базового C++ класса в Python – УСПЕХ

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

finch@laptop:~/work/swig_example$ python3
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] на linux
Введите "help", "copyright", "credits" или "license" для получения дополнительной информации.
>>> import example4
>>> b = example4.EventProcessor()
>>> b.getValue()
42
>>> b.setValue( 20 )
>>> b.getValue()
20
>>> example4.registerEventProcessor( 30, b )
>>> b.getValue()
30

Использование производного класса Python – НЕУДАЧА

Производный класс Python представлен следующим файлом.

derived4.py
import example4

class SOFProcessor( example4.EventProcessor ):
  def __init__( self ):
    print( "SOFProcessor.ctor" )

Следующее демонстрирует, что я могу создать экземпляр производного класса Python.

finch@laptop:~/work/swig_example$ python3
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] на linux
Введите "help", "copyright", "credits" или "license" для получения дополнительной информации.
>>> import example4
>>> import derived4
>>> d = derived4.SOFProcessor()
SOFProcessor.ctor

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

>>> d.getValue()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/finch/work/swig_example/example4.py", line 72, in getValue
    return _example4.EventProcessor_getValue(self)
TypeError: в методе 'EventProcessor_getValue' аргумент 1 типа 'EventProcessor const *'

Я также не могу передать этот экземпляр в глобальную функцию.

>>> example4.registerEventProcessor( 100, d )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/finch/work/swig_example/example4.py", line 83, in registerEventProcessor
    return _example4.registerEventProcessor(packetId, pProc)
TypeError: в методе 'registerEventProcessor' аргумент 2 типа 'EventProcessor *'

typedef для переопределения … чего-то?

Существует ли typedef, который я могу определить в файле интерфейса SWIG, чтобы экземпляр производного класса Python можно было правильно обернуть и передать на сторону C++?

Ответ

Да, можно создать класс на Python, который наследует от класса C++, обернутого с помощью SWIG, но это требует некоторых дополнительных шагов. Ваша ошибка возникает потому, что SWIG не обрабатывает производные классы, как ожидается. Вам нужно будет изменить ваш интерфейсный файл .swg, чтобы явно сообщить SWIG о том, что вы хотите сделать.

Вот шаги, которые можете выполнить, чтобы правильно реализовать наследование:

Изменение SWIG интерфейса

Вам нужно добавить некоторые дополнительные директивы в ваш интерфейсный файл example4.swg, чтобы поддержать наследование. Вот обновленный файл:

%module example4

%{
  #define SWIG_FILE_WITH_INIT
  #include "example4.h"
%}

// Указываем SWIG, что мы хотим использовать наследование
%include "std_string.i"
%include "example4.h"

// Объявляем, что Python может наследовать EventProcessor
%{
class SOFProcessor : public EventProcessor {
public:
  SOFProcessor() : EventProcessor() {}
};
%}

%include "example4.h"

// Теперь мы регистрируем наш новый класс для Python
%rename(SOFProcessor) SOFProcessor;

Изменение кода Python

Теперь вам нужно немного изменить ваш файл derived4.py:

import example4

class SOFProcessor(example4.EventProcessor):
    def __init__(self):
        print("SOFProcessor.ctor")
        super().__init__()  # Инициализируем базовый класс

# Проверьте создание экземпляра
if __name__ == "__main__":
    d = SOFProcessor()
    print(d.getValue())  # Теперь это должно работать
    example4.registerEventProcessor(100, d)
    print(d.getValue())

Сборка и тестирование

Теперь вы должны заново сгенерировать обертки и собрать модуль:

swig -c++ -python example4.swg
python3 example4_setup.py build_ext --inplace

После этого снова протестируйте код на Python:

import example4
import derived4

d = derived4.SOFProcessor()  # Создаем объект производного класса
print(d.getValue())           # Получаем значение
example4.registerEventProcessor(100, d)  # Передаем объект в C++
print(d.getValue())           # Получаем обновленное значение

Заключение

С помощью этих изменений Python класс SOFProcessor теперь должен корректно унаследовать функциональность от C++ класса EventProcessor. Вы сможете вызывать функции родительского класса и передавать экземпляры производного класса в функции C++. Убедитесь, что вы также правильно вызываете конструктор родительского класса внутри конструктора производного класса для инициализации.

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

Капча загружается...