4

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gunjan49
  • 53
  • 1
  • 5
  • Did you already have a look at the SWIG documentation on this topic? (http://www.swig.org/Doc2.0/SWIGDocumentation.html#Customization_exception) – gecco Nov 10 '11 at 19:36
  • You might want to make it a bit more obvious that you're using SWIG's director feature - I missed it the first time I read this question, and there's nothing to hint it at all in the code you've shown. – Flexo Nov 24 '11 at 00:50
  • @gecco - that documentation doesn't apply to exceptions thrown by SWIG Python code from directors (i.e. `director:except`) in much detail at all. – Flexo Nov 24 '11 at 01:08

1 Answers1

3

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.

Community
  • 1
  • 1
Flexo
  • 87,323
  • 22
  • 191
  • 272
  • can you plz show how can I handle the exception if I have one more exception class named class AnotherException : public std::exception {}; How do I distinguish which exception is thrown from python code and what piece of code will be added to handle both types of exception. Thanxx in advance – Gunjan49 Dec 19 '11 at 11:37
  • @Gunjan49 - I updated the example to show how you might handle multiple types of exceptions. I think this is the simplest way, it uses the SWIG type system to figure out what type it was. If you have a polymorphic type hierarchy you could exploit that here. The caller can't distinguish where the exception came from - that's the nice thing about it. (If you want to figure out where it came from that suggests you're doing something wrong to me, but you *could* set a flag on the exception from the `director:except` code to indicate the origin if you really wanted to do it) – Flexo Dec 19 '11 at 17:20