Поймать исключение Python в C ++ - PullRequest
4 голосов
/ 10 ноября 2011

Я разрабатываю приложение сервер-клиент, в котором клиент вызывает API-интерфейс сервера, который предоставляет интерфейс Python для ввода данных пользователем. Это означает, что интерфейс клиента и интерфейс сервера написаны на Python, а код сокета - на C ++.

На стороне сервера: -

У меня есть класс Test в C ++, и этот класс наследуется в Python с именем TestPython с использованием функции директора SWIG . Также у меня есть класс исключения MyException в C ++.

Теперь функция класса TestPython выдает MyException() из кода Python.

Я хочу обработать исключение, выброшенное из Python в коде C ++, используя SWIG.

Ниже приведен фрагмент кода:

C ++ Code-

class MyException
{
   public:
     string errMsg;
     MyException();
     MyException(string);
     ~MyException();
};

class Test
{
    int value;
    public:
      void TestException(int val);
      Test(int);
};

Код Python -

class TestPython(Test):
   def __init__(self):
     Test.__init__(self)

   def TestException(self,val):
     if val > 20:   
       throw MyException("MyException : Value Exceeded !!!")   
     else:    
       print "Value passed = ",val

Теперь, если вызывается функция TestException(), она должна выдать MyException. Я хочу обработать это MyException() исключение в моем коде C ++.

Так может кто-нибудь предложить мне, как это сделать, я имею в виду, что я должен написать в моем * .i (интерфейсном) файле, чтобы справиться с этим.

Вышеупомянутый TestException(), написанный на Python, вызывается клиентом, поэтому я должен уведомить клиента, если какое-либо исключение выдается сервером.

1 Ответ

3 голосов
/ 24 ноября 2011

Для этого вам в основном нужно написать %feature("director:except"), который может обработать исключение Python и перезапустить его как C ++. Вот небольшой, но полный пример:

Предположим, у нас есть следующий заголовочный файл, который мы хотим обернуть:

#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"

Который обрабатывает ошибку из вызова Director, проверяет, был ли это один из наших MyException экземпляров, а затем повторно выдает указатель, если он был. Если у вас есть несколько типов исключений, вам, вероятно, потребуется использовать PyErr_ExceptionMatches, чтобы определить, какой это тип.

Мы можем также бросить по значению или ссылке, используя:

  // Throw by value (after a copy!)
  MyException temp = *e;
  if (SWIG_IsNewObj(res)) 
    delete e;
  throw temp;

вместо этого, но учтите, что если вы бросите в Python подкласс из MyException, это приведет к нарушению проблемы среза объектов .

Я не совсем уверен, что код на 100% правильный, в частности, я считаю, что подсчет ссылок правильный, но я могу ошибаться.

Примечание. Чтобы этот пример работал (%pythonabc не сработал бы в противном случае), мне пришлось вызвать SWIG с -py3. Это, в свою очередь, означало, что мне пришлось перейти на SWIG 2.0, потому что моя установленная копия Python 3.2 удалила некоторые устаревшие функции из C-API, который вызывал SWIG 1.3.40.

...