Skip to content

Commit

Permalink
Add the class PyObjectPtr to manage reference counting.
Browse files Browse the repository at this point in the history
  • Loading branch information
bcoconni committed Nov 8, 2024
1 parent c9896bd commit 2719a00
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 34 deletions.
46 changes: 16 additions & 30 deletions python/PyLogger.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ PyLogger::PyLogger(PyObject* logger)
{
if (PyObject_IsInstance(logger, FGLogger_PyClass)) {
logger_pyclass = logger;
Py_INCREF(logger_pyclass);
} else {
PyErr_SetString(PyExc_TypeError, "The logger must be an instance of FGLogger");
}
Expand All @@ -50,7 +49,7 @@ PyLogger::PyLogger(PyObject* logger)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void PyLogger::SetLevel(LogLevel level) {
PyObject* py_level;
PyObjectPtr py_level;

switch (level)
{
Expand All @@ -74,38 +73,33 @@ void PyLogger::SetLevel(LogLevel level) {
break;
}

bool success = CallPythonMethodWithArguments("set_level", py_level);
if (success) FGLogger::SetLevel(level);
Py_DECREF(py_level);
PyObjectPtr result = CallPythonMethodWithArguments("set_level", py_level);
if (result) FGLogger::SetLevel(level);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void PyLogger::FileLocation(const std::string& filename, int line)
{
PyObject* py_filename = PyUnicode_FromString(filename.c_str());
PyObject* py_line = PyLong_FromLong(line);
PyObject* args = PyTuple_Pack(2, py_filename, py_line);
PyObjectPtr py_filename = PyUnicode_FromString(filename.c_str());
PyObjectPtr py_line = PyLong_FromLong(line);
PyObjectPtr args = PyTuple_Pack(2, py_filename.get(), py_line.get());
CallPythonMethodWithTuple("file_location", args);
Py_DECREF(args);
Py_DECREF(py_filename);
Py_DECREF(py_line);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void PyLogger::Message(const std::string& message)
{
PyObject* msg = PyUnicode_FromString(message.c_str());
PyObjectPtr msg = PyUnicode_FromString(message.c_str());
CallPythonMethodWithArguments("message", msg);
Py_DECREF(msg);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void PyLogger::Format(LogFormat format)
{
PyObject* py_format;
PyObjectPtr py_format;

switch (format)
{
Expand Down Expand Up @@ -142,35 +136,27 @@ void PyLogger::Format(LogFormat format)
}

CallPythonMethodWithArguments("format", py_format);
Py_DECREF(py_format);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool PyLogger::CallPythonMethodWithArguments(const char* method_name, PyObject* arg)
PyObjectPtr PyLogger::CallPythonMethodWithArguments(const char* method_name, const PyObjectPtr& arg)
{
PyObject* tuple = PyTuple_Pack(1, arg);
bool success = CallPythonMethodWithTuple(method_name, tuple);
Py_DECREF(tuple);
return success;
PyObjectPtr tuple = PyTuple_Pack(1, arg.get());
return CallPythonMethodWithTuple(method_name, tuple);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool PyLogger::CallPythonMethodWithTuple(const char* method_name, PyObject* tuple)
PyObjectPtr PyLogger::CallPythonMethodWithTuple(const char* method_name, const PyObjectPtr& tuple)
{
PyObject* method = PyObject_GetAttrString(logger_pyclass, method_name);
PyObjectPtr method = PyObject_GetAttrString(logger_pyclass.get(), method_name);
assert(method); // This should not fail as the constructor has checked the type of logger_pyclass.

PyObject* result = PyObject_CallObject(method, tuple);
Py_DECREF(method);
PyObjectPtr result = PyObject_CallObject(method.get(), tuple.get());

if (result) {
Py_DECREF(result);
return true;
}
if (!result) PyErr_Print();

PyErr_Print();
return false;
return result;
}
} // namespace JSBSim
62 changes: 58 additions & 4 deletions python/PyLogger.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,76 @@ extern PyObject* FGLogger_PyClass;
extern PyObject* LogLevel_PyClass;
extern PyObject* LogFormat_PyClass;

// Helper class to manage the reference count of a PyObject.
class PyObjectPtr {
public:
PyObjectPtr(PyObject* obj = nullptr) noexcept
: object(obj) {}

// Copy constructor
PyObjectPtr(const PyObjectPtr& other) noexcept {
object = other.object;
Py_XINCREF(object);
}

// Move constructor
PyObjectPtr(PyObjectPtr&& other) noexcept
: object(other.object)
{
other.object = nullptr;
}

// Copy assignment operator
PyObjectPtr& operator=(const PyObjectPtr& other) noexcept {
if (this != &other) {
Py_XDECREF(object); // Decrement the reference count of the current object
object = other.object;
Py_XINCREF(object); // Increment the reference count of the new object
}
return *this;
}

PyObjectPtr& operator=(PyObject* src) noexcept {
Py_XDECREF(object);
object = src;
return *this;
}

// Move assignment operator
PyObjectPtr& operator=(PyObjectPtr&& other) noexcept {
if (this != &other) {
Py_XDECREF(object); // Decrement the reference count of the current object
object = other.object;
other.object = nullptr; // Prevent the source from decrementing the reference count
}
return *this;
}

~PyObjectPtr() noexcept { Py_XDECREF(object); }

PyObject* get() const noexcept { return object; }
operator bool() const noexcept { return object != nullptr; }

protected:
PyObject* object;
};


class PyLogger : public FGLogger
{
public:
explicit PyLogger(PyObject* logger);
~PyLogger() override { Py_XDECREF(logger_pyclass); }
void SetLevel(LogLevel level) override;
void FileLocation(const std::string& filename, int line) override;
void Message(const std::string& message) override;
void Format(LogFormat format) override;
void Flush(void) override { CallPythonMethodWithTuple("flush", nullptr); }

private:
bool CallPythonMethodWithTuple(const char* method_name, PyObject* tuple);
bool CallPythonMethodWithArguments(const char* method_name, PyObject* arg);
PyObjectPtr CallPythonMethodWithTuple(const char* method_name, const PyObjectPtr& tuple);
PyObjectPtr CallPythonMethodWithArguments(const char* method_name, const PyObjectPtr& arg);

PyObject* logger_pyclass = nullptr;
PyObjectPtr logger_pyclass;
};
}
#endif

0 comments on commit 2719a00

Please sign in to comment.