boost::python Export Custom Exception

后端 未结 4 1206
天涯浪人
天涯浪人 2020-12-13 03:24

I am currently writing a C++ extension for Python using Boost.Python. A function in this extension may generate an exception containing information about the error (beyond

4条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-13 03:51

    The solution is to create your exception class like any normal C++ class

    class MyCPPException : public std::exception {...}
    

    The trick is that all boost::python::class_ instances hold a reference to the object's type which is accessible through their ptr() function. You can get this as you register the class with boost::python like so:

    class_ myCPPExceptionClass("MyCPPException"...);
    PyObject *myCPPExceptionType=myCPPExceptionClass.ptr();
    register_exception_translator(&translateFunc);
    

    Finally, when you are translating the C++ exception to a Python exception, you do so as follows:

    void translate(MyCPPException const &e)
    {
        PyErr_SetObject(myCPPExceptionType, boost::python::object(e).ptr());
    }
    

    Here is a full working example:

    #include 
    #include 
    #include 
    
    class MyCPPException : public std::exception
    {
    private:
      std::string message;
      std::string extraData;
    public:
      MyCPPException(std::string message, std::string extraData)
      {
        this->message = message;
        this->extraData = extraData;
      }
      const char *what() const throw()
      {
        return this->message.c_str();
      }
      ~MyCPPException() throw()
      {
      }
      std::string getMessage()
      {
        return this->message;
      }
      std::string getExtraData()
      {
        return this->extraData;
      }
    };
    
    void my_cpp_function(bool throwException)
    {
      std::cout << "Called a C++ function." << std::endl;
      if (throwException)
        {
          throw MyCPPException("Throwing an exception as requested.",
                   "This is the extra data.");
        }
    }
    
    PyObject *myCPPExceptionType = NULL;
    
    void translateMyCPPException(MyCPPException const &e)
    {
      assert(myCPPExceptionType != NULL);
      boost::python::object pythonExceptionInstance(e);
      PyErr_SetObject(myCPPExceptionType, pythonExceptionInstance.ptr());
    }
    
    BOOST_PYTHON_MODULE(my_cpp_extension)
    {
      boost::python::class_
        myCPPExceptionClass("MyCPPException",
                boost::python::init());
      myCPPExceptionClass.add_property("message", &MyCPPException::getMessage)
        .add_property("extra_data", &MyCPPException::getExtraData);
      myCPPExceptionType = myCPPExceptionClass.ptr();
      boost::python::register_exception_translator
        (&translateMyCPPException);
      boost::python::def("my_cpp_function", &my_cpp_function);
    }
    

    Here is the Python code that calls the extension:

    import my_cpp_extension
    try:
        my_cpp_extension.my_cpp_function(False)
        print 'This line should be reached as no exception should be thrown.'
    except my_cpp_extension.MyCPPException, e:
        print 'Message:', e.message
        print 'Extra data:',e.extra_data
    
    try:
        my_cpp_extension.my_cpp_function(True)
        print ('This line should not be reached as an exception should have been' +
           'thrown by now.')
    except my_cpp_extension.MyCPPException, e:
        print 'Message:', e.message
        print 'Extra data:',e.extra_data
    

提交回复
热议问题