Catching a Python exception in C++

让人想犯罪 __ 提交于 2019-12-06 06:11:23

问题


I am developing a server-client application in which the client calls a server's API which gives a Python interface for user input. It means the client interface and server interface is written in Python whereas the socket code is in C++.

On the server side:-

I have a class, Test, in C++ and this class is inherited in Python named TestPython using director feature of SWIG. Also I have an exception class MyException in C++.

Now a function of TestPython class throws MyException() from Python code.

I want to handle exception thrown from Python in C++ code using SWIG.

Below is code snippet:

C++ Code-

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

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

Python Code -

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

Now, if the TestException() function is called, it should throw MyException. I want to handle this MyException() exception in my C++ code.

So can anyone suggest my how to do that, I mean what should I write in my *.i(interface) file to handle this.

The above TestException() written in Python is called by the client, so I have to notify the client if any exception is thrown by the server.


回答1:


To do this you basically need to write a %feature("director:except") that can handle a Python exception and re-throw it as a C++ one. Here's a small but complete example:

Suppose we have the following header file we wish to wrap:

#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(); }

And this Python code that uses it:

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)

We can define the following SWIG interface file:

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

Which handles an error from a director call, looks to see if it was one of our MyException instances and then re-throws the pointer if it was. If you have multiple types of exception being thrown then you will probably need to use PyErr_ExceptionMatches to work out what type it is first.

We could throw also by value or reference using:

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

instead, but note that if you threw a subclass of MyException in Python this would fall foul of the object slicing problem.

I'm not quite sure if the code is 100% correct - in particular I think the reference counting is correct, but I could be wrong.

Note: In order to make this example work (%pythonabc wouldn't work otherwise) I had to call SWIG with -py3. This in turn meant I had to upgrade to SWIG 2.0, because my installed copy of Python 3.2 had removed some deprecated functions from the C-API that SWIG 1.3.40 called.



来源:https://stackoverflow.com/questions/8084353/catching-a-python-exception-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!