Я использую версию 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++. Убедитесь, что вы также правильно вызываете конструктор родительского класса внутри конструктора производного класса для инициализации.