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.
解决方案
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
#include
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.