Добавление swig pythoncode для установки этого флага на объекте Python

У меня есть контейнер класса 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

Это не настоящий код Python. Наличие минимальный воспроизводимый пример помогает, но в этом случае обратите внимание на директиву % newobject SWIG.

Mark Tolonen 11.05.2018 06:06

Очень красивый MCVE! Однако я не уверен в решении.

Mark Tolonen 16.05.2018 03:53

Итак, вы хотите, чтобы addObject автоматически украл созданный Python MyObject? Вы требуете, чтобы объекты MyContainer могли создаваться как кодом C++, так и кодом Python или только кодом Python? Что вы хотите, чтобы произошло, если вы для начала используете Python для добавления экземпляра, пришедшего из C++?

Flexo 22.05.2018 23:46

Да, именно так, функция addObject должна установить флаг thisown в ноль для объекта, скрытого от пользователя. Объект будет уничтожен, когда контейнер будет уничтожен, или решит уничтожить его объекты. Поскольку и MyObject, и MyContainer происходят из C++, не имеет значения, передает ли python MyContainer из C++ или создает его.

Totte Karlsson 22.05.2018 23:55

Ну, куда я направлялся: а) могу ли я просто автоматически скопировать все, что передается на addObject? б) могу ли я сделать MyContainer ссылкой на PyObject за всем, что к нему добавляется. (Оба метода - изящные, не считая настройки thisown, так что я могу попробовать записать все 3 варианта).

Flexo 22.05.2018 23:57

Конструкция копирования может быть хорошей, хотя я считаю, что в этом нет необходимости? В приведенном выше коде ничего не копирует конструкцию, но есть уродливый флаг thisown :( Контейнер будет передан на сторону C++, и когда это так, он должен вести себя так же. Не уверен, что означает удержание ссылки при передаче в C++?

Totte Karlsson 23.05.2018 00:04
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
6
770
1

Ответы 1

Вы ищете % 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, я полагаю, что ваш ответ правильный, поэтому я проголосовал за него.

Totte Karlsson 11.05.2018 17:12

@TotteKarlsson предоставил минимальный воспроизводимый пример. Ваш псевдокод не имеет смысла. Если ваш код правильно подсчитан, объект не будет освобожден.

Mark Tolonen 11.05.2018 17:30

Я добавил минимальный и поддающийся проверке пример, поскольку исходный опубликованный код действительно не имел смысла.

Totte Karlsson 11.05.2018 21:46

Другие вопросы по теме