Files
Pyclassifiers/pyclfs/PyHelper.hpp

232 lines
5.8 KiB
C++

#ifndef PYHELPER_HPP
#define PYHELPER_HPP
#pragma once
// Code taken and adapted from
// https ://www.codeproject.com/Articles/820116/Embedding-Python-program-in-a-C-Cplusplus-code
#include "boost/python/detail/wrap_python.hpp"
#include <boost/python/numpy.hpp>
#include <iostream>
namespace pywrap {
namespace p = boost::python;
namespace np = boost::python::numpy;
class CPyInstance {
public:
CPyInstance()
{
Py_Initialize();
np::initialize();
}
~CPyInstance()
{
Py_Finalize();
}
};
class CPyObject {
private:
PyObject* p;
public:
CPyObject() : p(nullptr)
{
}
CPyObject(PyObject* _p) : p(_p)
{
}
// Copy constructor
CPyObject(const CPyObject& other) : p(other.p)
{
if (p) {
Py_INCREF(p);
}
}
// Move constructor
CPyObject(CPyObject&& other) noexcept : p(other.p)
{
other.p = nullptr;
}
~CPyObject()
{
Release();
}
PyObject* getObject()
{
return p;
}
PyObject* setObject(PyObject* _p)
{
if (p != _p) {
Release(); // Release old reference
p = _p;
}
return p;
}
PyObject* AddRef()
{
if (p) {
Py_INCREF(p);
}
return p;
}
void Release()
{
if (p) {
Py_XDECREF(p);
p = nullptr;
}
}
PyObject* operator ->()
{
return p;
}
bool is() const
{
return p != nullptr;
}
// Check if object is valid
bool isValid() const
{
return p != nullptr;
}
operator PyObject* ()
{
return p;
}
// Copy assignment operator
CPyObject& operator=(const CPyObject& other)
{
if (this != &other) {
Release(); // Release current reference
p = other.p;
if (p) {
Py_INCREF(p); // Add reference to new object
}
}
return *this;
}
// Move assignment operator
CPyObject& operator=(CPyObject&& other) noexcept
{
if (this != &other) {
Release(); // Release current reference
p = other.p;
other.p = nullptr;
}
return *this;
}
// Assignment from PyObject* - DEPRECATED, use setObject() instead
PyObject* operator=(PyObject* pp)
{
setObject(pp);
return p;
}
explicit operator bool() const
{
return p != nullptr;
}
};
// RAII guard for PyObject* - safer alternative to manual reference management
class PyObjectGuard {
private:
PyObject* obj_;
bool owns_reference_;
public:
// Constructor takes ownership of a new reference
explicit PyObjectGuard(PyObject* obj = nullptr) : obj_(obj), owns_reference_(true) {}
// Constructor for borrowed references
PyObjectGuard(PyObject* obj, bool borrow) : obj_(obj), owns_reference_(!borrow) {
if (borrow && obj_) {
Py_INCREF(obj_);
owns_reference_ = true;
}
}
// Non-copyable to prevent accidental reference issues
PyObjectGuard(const PyObjectGuard&) = delete;
PyObjectGuard& operator=(const PyObjectGuard&) = delete;
// Movable
PyObjectGuard(PyObjectGuard&& other) noexcept
: obj_(other.obj_), owns_reference_(other.owns_reference_) {
other.obj_ = nullptr;
other.owns_reference_ = false;
}
PyObjectGuard& operator=(PyObjectGuard&& other) noexcept {
if (this != &other) {
reset();
obj_ = other.obj_;
owns_reference_ = other.owns_reference_;
other.obj_ = nullptr;
other.owns_reference_ = false;
}
return *this;
}
~PyObjectGuard() {
reset();
}
// Reset to nullptr, releasing current reference if owned
void reset(PyObject* new_obj = nullptr) {
if (owns_reference_ && obj_) {
Py_DECREF(obj_);
}
obj_ = new_obj;
owns_reference_ = (new_obj != nullptr);
}
// Release ownership and return the object
PyObject* release() {
PyObject* result = obj_;
obj_ = nullptr;
owns_reference_ = false;
return result;
}
// Get the raw pointer (does not transfer ownership)
PyObject* get() const {
return obj_;
}
// Check if valid
bool isValid() const {
return obj_ != nullptr;
}
explicit operator bool() const {
return obj_ != nullptr;
}
// Access operators
PyObject* operator->() const {
return obj_;
}
// Implicit conversion to PyObject* for API calls (does not transfer ownership)
operator PyObject*() const {
return obj_;
}
};
// Helper function to create a PyObjectGuard from a borrowed reference
inline PyObjectGuard borrowReference(PyObject* obj) {
return PyObjectGuard(obj, true);
}
// Helper function to create a PyObjectGuard from a new reference
inline PyObjectGuard newReference(PyObject* obj) {
return PyObjectGuard(obj);
}
} /* namespace pywrap */
#endif