C++ 动态重定向自定义C++;使用SWIG将异常作为Python异常
形势 <>我想用SWIG为C++ API创建Python语言绑定。某些API函数可能引发异常。C++应用程序具有自定义异常的层次结构,例如这个例子:C++ 动态重定向自定义C++;使用SWIG将异常作为Python异常,c++,python,exception,reflection,swig,C++,Python,Exception,Reflection,Swig,形势 我想用SWIG为C++ API创建Python语言绑定。某些API函数可能引发异常。C++应用程序具有自定义异常的层次结构,例如这个例子: std::exception -> API::Exception -> API::NetworkException -> API::TimeoutException -> API::UnreachableException -> API::InvalidAddressExcept
std::exception
-> API::Exception
-> API::NetworkException
-> API::TimeoutException
-> API::UnreachableException
-> API::InvalidAddressException
所需的行为如下所示:
// File: numbers.h
namespace numbers {
int fact(int n);
}
// File: numbers.cpp
#include "TooBigException.h"
namespace numbers {
int fact(int n) {
if (n > 10) throw TooBigException("Value too big", n);
else if (n <= 1) return 1;
else return n*fact(n-1);
}
}
// File: TooBigException.h
namespace numbers {
class TooBigException: public std::exception {
public:
explicit TooBigException(const std::string & inMessage,
const int inValue);
virtual ~TooBigException() throw() {}
virtual const char* what() const throw();
const std::string & message() const;
const int value() const;
private:
std::string mMessage;
int mValue;
};
}
// File: TooBigException.cpp
#include "TooBigException.h"
namespace numbers {
TooBigException::TooBigException(const std::string & inMessage, const int inValue):
std::exception(),
mMessage(inMessage),
mValue(inValue)
{
}
const char* TooBigException::what() const throw(){
return mMessage.c_str();
}
const std::string & TooBigException::message() const {
return mMessage;
}
const int TooBigException::value() const {
return mValue;
}
}
API::exception
在本例中)的包装器扩展了Object
,而不是BaseException
,Python中的所有异常都应该派生自该Python类
此外,似乎不可能让SWIG手动添加父类。注意:当通过使用%typemap(javabase)
将SWIG与Java结合使用时,这是可能的(有关详细信息,请参阅)PyErr\u SetString
。这也显示在下面的演示应用程序中
但对于Python的标准(内置)异常来说,这只是一个小问题,因为对它们的引用存储在Python C API的全局变量[]中
我知道有一种方法PyErr\u NewException
可以获取对自定义异常的引用,但我没有做到这一点为了解决这个问题,我创建了一个简单的C++ API,它具有一个函数,它计算一个数的阶乘。它有一个最小的自定义异常层次结构,只包含一个类
TooBigException
注意:此异常作为一般问题中的基本异常,应用程序应使用它的任何子类。这意味着解决方案只能使用捕获的异常的动态(即运行时)类型在Python中重新显示它(见下文)
演示应用程序的完整源代码如下:
// File: numbers.h
namespace numbers {
int fact(int n);
}
// File: numbers.cpp
#include "TooBigException.h"
namespace numbers {
int fact(int n) {
if (n > 10) throw TooBigException("Value too big", n);
else if (n <= 1) return 1;
else return n*fact(n-1);
}
}
// File: TooBigException.h
namespace numbers {
class TooBigException: public std::exception {
public:
explicit TooBigException(const std::string & inMessage,
const int inValue);
virtual ~TooBigException() throw() {}
virtual const char* what() const throw();
const std::string & message() const;
const int value() const;
private:
std::string mMessage;
int mValue;
};
}
// File: TooBigException.cpp
#include "TooBigException.h"
namespace numbers {
TooBigException::TooBigException(const std::string & inMessage, const int inValue):
std::exception(),
mMessage(inMessage),
mValue(inValue)
{
}
const char* TooBigException::what() const throw(){
return mMessage.c_str();
}
const std::string & TooBigException::message() const {
return mMessage;
}
const int TooBigException::value() const {
return mValue;
}
}
当前实施情况
我当前的实现仍然(成功)抛出了一个固定的标准Python异常。然后将上面的代码
替换为:
PyErr_SetString(PyExc_Exception, (std::string("C++ self-defined exception ") + e.what()).c_str());
return NULL;
这在Python中提供了以下(预期)行为:
>>> import numbers
>>> fact(11)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception: C++ self-defined exception Value too big
>>导入编号
>>>事实(11)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
异常:C++自定义异常值太大
似乎有人在swig用户列表上回答了您的基本问题
%exception {
try {
$action
} catch (MyException &_e) {
SWIG_Python_Raise(SWIG_NewPointerObj(
(new MyException(static_cast<const MyException& >(_e))),
SWIGTYPE_p_MyException,SWIG_POINTER_OWN),
"MyException", SWIGTYPE_p_MyException);
SWIG_fail;
}
}
%异常{
试一试{
$action
}捕获(MyException&_e){
SWIG_Python_Raise(SWIG_NewPointerObj(
(新的MyException(静态投射),
SWIGTYPE_p_MyException,SWIG_POINTER_OWN),
“MyException”,SWIGTYPE_p_MyException);
SWIG_失败;
}
}
我相信,这确实假设您已经为您的异常类生成了包装器 您的层次结构示例
std::exception
-> API::Exception
-> API::NetworkException
-> API::TimeoutException
-> API::UnreachableException
-> API::InvalidAddressException
例一:
%module example
%include "stl.i"
%include "exception.i"
%{
#define SWIG_FILE_WITH_INIT
#include "example.cpp"
%}
%{
#define CATCH_PE(Namespace,Exception) \
catch(const Namespace::Exception &e) \
{ \
SWIG_Python_Raise(SWIG_NewPointerObj(new Namespace::Exception(e), \
SWIGTYPE_p_##Namespace##__##Exception,SWIG_POINTER_OWN), \
#Exception, SWIGTYPE_p_##Namespace##__##Exception); \
SWIG_fail; \
} \
/**/
// should be in "derived first" order
#define FOR_EACH_EXCEPTION(ACTION) \
ACTION(API,UnreachableException) \
ACTION(API,TimeoutException) \
ACTION(API,InvalidAddressException) \
ACTION(API,NetworkException) \
ACTION(API,Exception) \
/**/
// In order to remove macros, need traits:
// http://swig.10945.n7.nabble.com/traits-based-access-to-swig-type-info-td12315.html
%}
%exception {
try {
$action
}
FOR_EACH_EXCEPTION(CATCH_PE)
catch (const std::exception & e)
{
SWIG_exception(SWIG_RuntimeError, (std::string("C++ std::exception: ") + e.what()).c_str());
}
catch (...)
{
SWIG_exception(SWIG_UnknownError, "C++ anonymous exception");
}
}
%include "example.cpp"
示例.cpp:
#include <exception>
#include <stdexcept>
namespace API
{
struct Exception: std::exception
{
virtual const char* what() const throw()
{
return "It is API::Exception";
}
};
struct NetworkException: Exception
{
virtual const char* what() const throw()
{
return "It is API::NetworkException";
}
};
struct TimeoutException: NetworkException
{
virtual const char* what() const throw()
{
return "It is API::TimeoutException";
}
};
struct UnreachableException: NetworkException
{
virtual const char* what() const throw()
{
return "It is API::UnreachableException";
}
};
struct InvalidAddressException: Exception
{
virtual const char* what() const throw()
{
return "It is API::InvalidAddressException";
}
};
inline void select(int i)
{
switch(i)
{
case 0: throw Exception();
case 1: throw NetworkException();
case 2: throw TimeoutException();
case 3: throw UnreachableException();
case 4: throw InvalidAddressException();
default: throw std::runtime_error("It is std::runtime_error");
}
}
}
test.py:
#!/usr/bin/env python2.7
from exceptions import BaseException
from example import *
def catch(i):
try:
select(i)
except UnreachableException as e:
print "Caught UnreachableException"
print e.what()
print e
except TimeoutException as e:
print "Caught TimeoutException"
print e.what()
print e
except InvalidAddressException as e:
print "Caught InvalidAddressException"
print e.what()
print e
except NetworkException as e:
print "Caught NetworkException"
print e.what()
print e
except Exception as e:
print "Caught Exception"
print e.what()
print e
except BaseException as e:
print "Caught BaseException"
print str(e)
print "_"*16
for i in xrange(6):
catch(i)
输出为:
Caught Exception
It is API::Exception
<example.Exception; proxy of <Swig Object of type 'API::Exception *' at 0x7f9f54a02120> >
________________
Caught NetworkException
It is API::NetworkException
<example.NetworkException; proxy of <Swig Object of type 'API::NetworkException *' at 0x7f9f54a02120> >
________________
Caught TimeoutException
It is API::TimeoutException
<example.TimeoutException; proxy of <Swig Object of type 'API::TimeoutException *' at 0x7f9f54a02120> >
________________
Caught UnreachableException
It is API::UnreachableException
<example.UnreachableException; proxy of <Swig Object of type 'API::UnreachableException *' at 0x7f9f54a02120> >
________________
Caught InvalidAddressException
It is API::InvalidAddressException
<example.InvalidAddressException; proxy of <Swig Object of type 'API::InvalidAddressException *' at 0x7f9f54a02120> >
________________
Caught BaseException
C++ std::exception: It is std::runtime_error
________________
捕获异常
它是API::Exception
________________
捕获的网络异常
它是API::NetworkException
________________
捕获的超时异常
它是API::TimeoutException
________________
捕获无法访问的异常
它是API::UnreachableException
________________
捕获无效地址异常
它是API::InvalidAddressException
________________
捕获的基本异常
C++ STD::异常:它是STD::运行时错误
________________
基于。我不太确定您想要什么,但您是否查看过该代码在第二种情况下是否有帮助<代码>抛出代码>这确实是答案的一部分。但是,正如问题中明确指出的,我希望使用动态类型,而此解决方案执行静态转换。换句话说:MyException的任何子类都将被捕获并更改为MyException,所有子类信息都将丢失。
#!/usr/bin/env python2.7
from exceptions import BaseException
from example import *
def catch(i):
try:
select(i)
except UnreachableException as e:
print "Caught UnreachableException"
print e.what()
print e
except TimeoutException as e:
print "Caught TimeoutException"
print e.what()
print e
except InvalidAddressException as e:
print "Caught InvalidAddressException"
print e.what()
print e
except NetworkException as e:
print "Caught NetworkException"
print e.what()
print e
except Exception as e:
print "Caught Exception"
print e.what()
print e
except BaseException as e:
print "Caught BaseException"
print str(e)
print "_"*16
for i in xrange(6):
catch(i)
Caught Exception
It is API::Exception
<example.Exception; proxy of <Swig Object of type 'API::Exception *' at 0x7f9f54a02120> >
________________
Caught NetworkException
It is API::NetworkException
<example.NetworkException; proxy of <Swig Object of type 'API::NetworkException *' at 0x7f9f54a02120> >
________________
Caught TimeoutException
It is API::TimeoutException
<example.TimeoutException; proxy of <Swig Object of type 'API::TimeoutException *' at 0x7f9f54a02120> >
________________
Caught UnreachableException
It is API::UnreachableException
<example.UnreachableException; proxy of <Swig Object of type 'API::UnreachableException *' at 0x7f9f54a02120> >
________________
Caught InvalidAddressException
It is API::InvalidAddressException
<example.InvalidAddressException; proxy of <Swig Object of type 'API::InvalidAddressException *' at 0x7f9f54a02120> >
________________
Caught BaseException
C++ std::exception: It is std::runtime_error
________________