在 C++ 中捕獲 Python 異常

華科雲商小雪發表於2024-02-05

在 C++ 中捕獲 Python 異常的原理涉及到 Python C API 的使用和異常處理機制。下面簡要介紹捕獲 Python 異常的原理:Python C API 允許 C++ 程式碼與 Python 直譯器進行互動,從而可以在 C++ 中呼叫 Python 函式、獲取 Python 物件、捕獲 Python 異常等操作。所以說能都捕獲異常並做提示,針對後期程式碼最佳化有很大的幫助,下面就看看具體的解決方案吧。

1、問題背景

在開發一個伺服器-客戶端應用時,客戶端會呼叫伺服器的 API,該 API 提供了用於使用者輸入的 Python 介面。這意味著客戶端介面和伺服器介面是用 Python 編寫的,而套接字程式碼是用 C++ 編寫的。

在伺服器端,我有一個 C++ 類的 Test,我們用 SWIG 的管理機制在 Python 中繼承 Test,命名為 TestPython。我還定義一個 C++ 中的異常類 MyException。現在,TestPython 類的一個函式從 Python 程式碼中丟擲了 MyException()。

我希望在 C++ 程式碼中使用 SWIG 來處理從 Python 中丟擲的異常。因此,我想知道應該在 *.i(介面)檔案中寫什麼來處理這種情況。

2、解決方案

為了實現這個功能,您需要編寫一個 %feature("director:except"),它可以處理 Python 異常並將其重新丟擲為 C++ 異常。以下是一個簡單但完整的示例:

標頭檔案 example.h

#include <iostream>

#include <exception>

class MyException : public std::exception {
};

class AnotherException : public std::exception {
};

class Callback {
public:
       virtual ~Callback() { std::cout << "~Callback()" << std:: endl; }
       virtual void run() { std::cout << "Callback::run()" << std::endl; }
};

inline void call(Callback *callback) { if (callback) callback->run(); }

Python 程式碼

import example 


class PyCallback(example.Callback):
   def __init__(self):
       example.Callback.__init__(self)
   def run(self):
       print("PyCallback.run()")
       raise example.MyException()

callback = PyCallback()
example.call(callback)

SWIG 介面檔案

%module(directors="1") example

%{
#include "example.h"
%}

%include "std_string.i"
%include "std_except.i"
%include "pyabc.i"

// Python requires that anything we raise inherits from this
%pythonabc(MyException, Exception);

%feature("director:except") {
   PyObject *etype = $error;
   if (etype != NULL) {
     PyObject *obj, *trace;
     PyErr_Fetch(&etype, &obj, &trace);
     Py_DecRef(etype);
     Py_DecRef(trace);
     // Not too sure if I need to call Py_DecRef for obj

     void *ptr;
     int res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_MyException, 0);
     if (SWIG_IsOK(res) && ptr) {
       MyException *e = reinterpret_cast< MyException * >(ptr);
       // Throw by pointer (Yucky!)
       throw e;
     }

     res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_AnotherException, 0);
     if (SWIG_IsOK(res) && ptr) {
       AnotherException *e = reinterpret_cast< AnotherException * >(ptr);
       throw e;
     }

     throw Swig::DirectorMethodException();
   }
}

%feature("director") Callback;
%include "example.h"

程式碼會處理來自管理函式的錯誤,檢查它是否是我們 MyException 例項之一,如果是,則重新丟擲指標。如果您有多種型別的異常被丟擲,那麼您可能需要先使用 PyErr_ExceptionMatches 來計算出它的型別。

透過呼叫 SWIG 使用 -py3 引數,我們就可以讓這個示例工作(否則 %pythonabc 不起作用)。這又意味著我們必須升級到 SWIG 2.0,因為我安裝的 Python 3.2 從 C-API 中刪除了一些 SWIG 1.3.40 呼叫的已棄用的函式。

在呼叫 Python 函式後,可以使用 PyErr_Occurred() 檢查是否發生了異常,並使用 PyErr_Print() 列印異常資訊。在實際應用中,你可能需要根據你的需求進行更詳細的異常處理。

此外,要確保在 C++ 程式碼中正確處理 Python 的引用計數,避免記憶體洩漏,可以使用 Py_XDECREF 來遞減引用計數。以上就是今天的全部內容,如果有更好的學習技巧或者需要解答的地方,記得評論區留言討論。


來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70034537/viewspace-3006301/,如需轉載,請註明出處,否則將追究法律責任。

相關文章