25

現在、Boost.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

残念ながら、Boost.Python はすべての C++ 例外 (のサブクラスstd::exception) を に変換するようRuntimeErrorです。Boost.Python を使用すると、カスタムの例外変換を実装できますPyErr_SetObjectが、PyObject*(例外の型の場合) とPyObject*(例外の値の場合) を使用する必要があります。どちらも、Boost から取得する方法がわかりません。 Python クラス。おそらく、私がまだ見つけていない方法 (それは素晴らしいことです) があります。それ以外の場合、PythonでキャッチできるようにカスタムC ++例外をエクスポートする方法を知っている人はいますか?

4

6 に答える 6

28

解決策は、通常の C++ クラスのように例外クラスを作成することです

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

秘訣は、すべての boost::python::class_ インスタンスが、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
于 2010-02-14T20:33:58.150 に答える
4

Jack Edmonds の回答では、継承しない Python の「例外」クラスException(またはその他の組み込みの Python 例外クラス) が定義されています。だからそれはキャッチすることができますが

except my_cpp_extension.MyCPPException as e:
    ...

いつものキャッチオールでは釣れない

except Exception as e:
    ...

を継承するカスタム Python 例外クラスを作成する方法を次に示しますException

于 2012-03-09T06:48:47.440 に答える
1

可変個引数テンプレートと一般化されたラムダ キャプチャのおかげで、Jack Edmond の回答をより管理しやすいものにまとめて、ユーザーからすべての面倒なことを隠すことができます。

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_

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

ここで、Boost.Python の優れた点に戻りclass_ます。インスタンスに名前を付ける必要はなくPyObject*、この extra は必要なく、どこかに追加の関数も必要ありません。

于 2016-06-13T21:36:05.053 に答える