boost::python导出自定义异常

2024-05-03 12:41:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我现在正在使用Posith.python编写Python的C++扩展。此扩展中的函数可能会生成一个异常,其中包含有关错误的信息(而不仅仅是描述发生了什么的人类可读字符串)。我希望我可以将这个异常导出到Python,这样我就可以捕获它并使用额外的信息做些事情。

例如:

import my_cpp_module
try:
    my_cpp_module.my_cpp_function()
except my_cpp_module.MyCPPException, e:
    print e.my_extra_data

不幸的是,Posith.Python似乎将所有C++异常(即^ {CD1>}的子类)转换为^ {CD2>}。我意识到Boost.Python允许实现自定义异常转换,但是,需要使用PyErr_SetObject,它接受一个PyObject*(对于异常类型)和一个PyObject*(对于异常值),这两个我都不知道如何从Boost.Python类中获取。也许有一种方法(这将是伟大的)我只是还没有找到。否则,有人知道如何导出自定义C++异常,以便我可以在Python中捕获它吗?


Tags: 函数字符串import信息my错误function人类
3条回答

多亏了可变模板和广义lambda捕获,我们可以将Jack Edmond's answer折叠成更易于管理的内容,并向用户隐藏所有问题:

template <class E, class... Policies, class... Args>
py::class_<E, Policies...> exception_(Args&&... args) {
    py::class_<E, Policies...> cls(std::forward<Args>(args)...);
    py::register_exception_translator<E>([ptr=cls.ptr()](E const& e){
        PyErr_SetObject(ptr, py::object(e).ptr());
    });
    return cls;
}

要将MyCPPException作为异常公开,只需更改py::class_exception_的绑定中的py::class_

exception_<MyCPPException>("MyCPPException", py::init<std::string, std::string>())
    .add_property("message", &MyCPPException::getMessage)
    .add_property("extra_data", &MyCPPException::getExtraData)
;

现在我们回到Boost.Python的细节:不需要命名class_实例,不需要这个额外的PyObject*,也不需要在某个地方使用额外的函数。

Jack Edmonds给出的答案定义了一个不继承Exception(或任何其他内置Python异常类)的Python“exception”类。所以尽管它可以

except my_cpp_extension.MyCPPException as e:
    ...

它不能像往常一样被抓住

except Exception as e:
    ...

Here是如何创建一个定制的Python异常类,该类将继承Exception

< >解决方法是创建异常类,就像任何普通C++类

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

诀窍在于,所有boost::python::class_u实例都持有对对象类型的引用,可以通过它们的ptr()函数访问该类型。您可以在使用boost::python注册类时获得此信息,如下所示:

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

最后,当您将C++异常转换为Python异常时,您这样做如下:

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

下面是一个完整的工作示例:

#include <boost/python.hpp>
#include <assert.h>
#include <iostream>

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_<MyCPPException>
    myCPPExceptionClass("MyCPPException",
            boost::python::init<std::string, std::string>());
  myCPPExceptionClass.add_property("message", &MyCPPException::getMessage)
    .add_property("extra_data", &MyCPPException::getExtraData);
  myCPPExceptionType = myCPPExceptionClass.ptr();
  boost::python::register_exception_translator<MyCPPException>
    (&translateMyCPPException);
  boost::python::def("my_cpp_function", &my_cpp_function);
}

下面是调用扩展的Python代码:

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

相关问题 更多 >