У меня есть контейнер класса C++, MyContainer, содержащий объекты типа MyObject, а также класс C++.
Ниже приведен код заголовка C++ (freemenot.h).
#ifndef freemenotH
#define freemenotH
#include <vector>
#include <string>
using std::string;
class MyObject
{
public:
MyObject(const string& lbl);
~MyObject();
string getLabel();
private:
string label;
};
class MyContainer
{
public:
MyContainer();
~MyContainer();
void addObject(MyObject* o);
MyObject* getObject(unsigned int t);
int getNrOfObjects();
private:
std::vector<MyObject*> mObjects;
};
#endif
а это источник (freemenot.cpp)
#include "freemenot.h"
#include <iostream>
using namespace std;
/* MyObject source */
MyObject::MyObject(const string& lbl)
:
label(lbl)
{ cout<<"In object ctor"<<endl; }
MyObject::~MyObject() { cout<<"In object dtor"<<endl; }
string MyObject::getLabel() { return label; }
/* MyContainer source */
MyContainer::MyContainer() { cout<<"In container ctor"<<endl; }
MyContainer::~MyContainer()
{
cout<<"In container dtor"<<endl;
for(unsigned int i = 0; i < mObjects.size(); i++)
{
delete mObjects[i];
}
}
int MyContainer::getNrOfObjects() { return mObjects.size(); }
void MyContainer::addObject(MyObject* o) { mObjects.push_back(o); }
MyObject* MyContainer::getObject(unsigned int i) { return mObjects[i]; }
Обратите внимание, что объекты хранятся в векторе как RAW УКАЗАТЕЛИ. Класс спроектирован таким образом, и контейнер, таким образом, отвечает за освобождение объектов в своем деструкторе, как это делается в деструкторах цикла for.
В коде C++, как показано ниже, объект o1 добавляется в контейнер c, который возвращается в клиентский код.
MyContainer* getAContainerWithSomeObjects()
{
MyContainer* c = new MyContainer();
MyObject* o1 = new MyObject();
c.add(o1);
return c;
}
Возвращенный контейнер владеет своими объектами и отвечает за освобождение этих объектов, когда это будет сделано. В C++ доступ к объектам контейнеров разрешен после выхода из функции, описанной выше.
Для предоставления указанных выше классов Python с помощью Swig потребуется файл интерфейса. Этот интерфейсный файл выглядит так
%module freemenot
%{ #include "freemenot.h" %}
%include "std_string.i"
//Expose to Python
%include "freemenot.h"
А для создания модуля Python с помощью CMake использовался следующий сценарий CMake.
cmake_minimum_required(VERSION 2.8)
project(freemenot)
find_package(SWIG REQUIRED)
include(UseSWIG)
find_package(PythonInterp)
find_package(PythonLibs)
get_filename_component(PYTHON_LIB_FOLDER ${PYTHON_LIBRARIES} DIRECTORY CACHE)
message("Python lib folder: " ${PYTHON_LIB_FOLDER})
message("Python include folder: " ${PYTHON_INCLUDE_DIRS})
message("Python libraries: " ${PYTHON_LIBRARIES})
set(PyModule "freemenot")
include_directories(
${PYTHON_INCLUDE_PATH}
${CMAKE_CURRENT_SOURCE_DIR}
)
link_directories( ${PYTHON_LIB_FOLDER})
set(CMAKE_MODULE_LINKER_FLAGS ${CMAKE_CURRENT_SOURCE_DIR}/${PyModule}.def)
set_source_files_properties(${PyModule}.i PROPERTIES CPLUSPLUS ON)
set_source_files_properties(${PyModule}.i PROPERTIES SWIG_FLAGS "-threads")
SWIG_ADD_LIBRARY(${PyModule}
MODULE LANGUAGE python
SOURCES ${PyModule}.i freemenot.cpp)
SWIG_LINK_LIBRARIES (${PyModule} ${PYTHON_LIB_FOLDER}/Python37_CG.lib )
# INSTALL PYTHON BINDINGS
# Get the python site packages directory by invoking python
execute_process(COMMAND python -c "import site; print(site.getsitepackages()[0])" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
message("PYTHON_SITE_PACKAGES = ${PYTHON_SITE_PACKAGES}")
install(
TARGETS _${PyModule}
DESTINATION ${PYTHON_SITE_PACKAGES})
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PyModule}.py
DESTINATION ${PYTHON_SITE_PACKAGES}
)
При создании файлов make с помощью CMake и компиляции с использованием компилятора borlands bcc32 модуль Python (Freemenot) создается и устанавливается в допустимую папку пакетов сайта python3.
Затем в Python можно использовать следующий скрипт для освещения проблемы.
import freemenot as fmn
def getContainer():
c = fmn.MyContainer()
o1 = fmn.MyObject("This is a label")
o1.thisown = 0
c.addObject(o1)
return c
c = getContainer()
print (c.getNrOfObjects())
#if the thisown flag for objects in the getContainer function
#is equal to 1, the following call return an undefined object
#If the flag is equal to 0, the following call will return a valid object
a = c.getObject(0)
print (a.getLabel())
Этот код Python может выглядеть нормально, но не работай, как и ожидалось. Проблема в том, что, когда функция getContainer () возвращает освобождается память для объекта o1, если флаг это вниз не установлен в ноль. Доступ к объекту после этой строки с использованием возвращенного контейнера приведет к катастрофе. Заметьте, в этом нет ничего плохого, поскольку именно так работает сборка мусора Python.
Для вышеуказанного варианта использования возможность установить для объектов Python это вниз флаг внутри в функции addObject сделает объекты C++ пригодными для использования в Python. Установка этого флага пользователем - не лучшее решение. Можно также расширить класс python с помощью функции «addObject» и изменить флаг thisown внутри этой функции и тем самым скрыть эту уловку с памятью от пользователя.
Вопрос в том, как заставить Swig сделать это, не расширяя класс? Я ищу typemap или, возможно, % pythoncode, но, похоже, не могу найти хороший рабочий пример.
Приведенный выше код должен использоваться и передаваться в программу C++, которая вызывает интерпретатор Python. Программа C++ отвечает за управление памятью, выделенной в функции python, даже после PyFinalize ().
Приведенный выше код можно скачать с github https://github.com/TotteKarlsson/miniprojects
Очень красивый MCVE! Однако я не уверен в решении.
Итак, вы хотите, чтобы addObject автоматически украл созданный Python MyObject? Вы требуете, чтобы объекты MyContainer могли создаваться как кодом C++, так и кодом Python или только кодом Python? Что вы хотите, чтобы произошло, если вы для начала используете Python для добавления экземпляра, пришедшего из C++?
Да, именно так, функция addObject должна установить флаг thisown в ноль для объекта, скрытого от пользователя. Объект будет уничтожен, когда контейнер будет уничтожен, или решит уничтожить его объекты. Поскольку и MyObject, и MyContainer происходят из C++, не имеет значения, передает ли python MyContainer из C++ или создает его.
Ну, куда я направлялся: а) могу ли я просто автоматически скопировать все, что передается на addObject? б) могу ли я сделать MyContainer ссылкой на PyObject за всем, что к нему добавляется. (Оба метода - изящные, не считая настройки thisown, так что я могу попробовать записать все 3 варианта).
Конструкция копирования может быть хорошей, хотя я считаю, что в этом нет необходимости? В приведенном выше коде ничего не копирует конструкцию, но есть уродливый флаг thisown :( Контейнер будет передан на сторону C++, и когда это так, он должен вести себя так же. Не уверен, что означает удержание ссылки при передаче в C++?






Вы ищете % newobject. Вот небольшой пример:
%module test
%newobject create;
%delobject destroy;
%inline %{
#include <iostream>
struct Test
{
Test() { std::cout << "create" << std::endl; }
~Test() { std::cout << "destroy" << std::endl; }
};
Test* create() { return new Test; }
void destroy(Test* t) { delete t; }
%}
Использовать:
>>> import test
>>> t1 = test.create() # create a test object
create
>>> t2 = test.Test() # don't really need a create function :)
create
>>> t3 = test.create() # and another.
create
>>> test.destroy(t2) # explicitly destroy one
destroy
>>>
>>>
>>>
>>> ^Z # exit Python and the other two get destroyed.
destroy
destroy
Собственно, я не этого хочу. Это по-прежнему требует, чтобы объекты были освобождены в Python. В моем случае указанная выше функция Python выполняется из программы C++, и после ее возврата памятью следует управлять на стороне C++. Вот почему я хочу установить флаг thisown для объектов, добавляемых в контейнер. Что касается «чистого» кода Python, я полагаю, что ваш ответ правильный, поэтому я проголосовал за него.
@TotteKarlsson предоставил минимальный воспроизводимый пример. Ваш псевдокод не имеет смысла. Если ваш код правильно подсчитан, объект не будет освобожден.
Я добавил минимальный и поддающийся проверке пример, поскольку исходный опубликованный код действительно не имел смысла.
Это не настоящий код Python. Наличие минимальный воспроизводимый пример помогает, но в этом случае обратите внимание на директиву % newobject SWIG.