在这个教程的第2部分中,我介绍了用于从C++解析Python异常的代码。在第3部分中,我使用Python ConfigParser
模块实现了一个简单的配置解析类。作为该实现的一部分,我提到对于任何规模的项目,人们都希望在类中捕获并处理Python异常,以便该类的客户不必了解Python的细节。从调用者的角度来看,这个类就像任何其他C++类一样。
处理Python异常的明显方法是在每个函数中处理它们。例如,我们创建的C++ ConfigParser类的get函数将变为:
std::string ConfigParser::get(const std::string &attr, const std::string §ion){
try{
return py::extract(conf_parser_.attr("get")(section, attr));
}catch(boost::python::error_already_set const &){
std::string perror_str = parse_python_exception();
throw std::runtime_error("Error getting configuration option: " + perror_str);
}
}
错误处理代码保持不变,但现在main
函数变为:
int main(){
Py_Initialize();
try{
ConfigParser parser;
parser.parse_file("conf_file.1.conf");
...
// Will raise a NoOption exception
cout << "Proxy host: " << parser.get("ProxyHost", "Network") << endl;
}catch(exception &e){
cout << "Here is the error, from a C++ exception: " << e.what() << endl;
}
}
当Python异常被抛出时,它将被解析并重新打包为一个std::runtime_error
,它在调用处被捕获并像正常的C++异常一样处理(即无需经历parse_python_exception
严格的操作)。对于只有少数函数或使用嵌入式Python的一两个类的项目,这肯定会有效。但是,对于更大的项目,人们希望避免大量重复的代码,它将不可避免地带来的错误。
对于我的实现,我想总是以相同的方式处理错误,但我需要一种方法来调用具有不同签名的不同函数。我决定利用boost
库的另一个强大的领域:仿函数库,特别是boost::bind
和boost::function
。boost::function
提供仿函数类包装器,boost::bind
绑定函数的参数。然后,这两者一起启用函数及其参数的传递,这些函数及其参数可以在以后调用。正是医生所要求的!
要使用仿函数,函数需要知道返回类型。由于我们使用不同的签名包装函数,因此函数模板可以很好地完成这一操作:
template
return_type call_python_func(boost::function to_call, const std::string &error_pre){
std::string error_str(error_pre);
try{
return to_call();
}catch(boost::python::error_already_set const &){
error_str = error_str + parse_python_exception();
throw std::runtime_error(error_str);
}
}
此函数将仿函数对象作为调用boost::python
函数的函数。每个调用boost::python
代码的函数现在被分成两个函数:私有的核心函数调用Python功能,公开的包装的函数使用call_python_func
函数。这是更新的get
函数及其合作伙伴:
string ConfigParser::get(const string &attr, const string §ion){
return call_python_func(boost::bind(&ConfigParser::get_py, this, attr, section),
"Error getting configuration option: ");
}
string ConfigParser::get_py(const string &attr, const string §ion){
return py::extract(conf_parser_.attr("get")(section, attr));
}
get
函数将传入的参数与隐式this指针绑定到get_py
函数,get_py·函数又调用
boost::python`执行操作所需的函数。简单有效。
当然,这里有一个权衡。不是重复的try...catch块代码和Python错误处理,每个类声明的函数数量增加了一倍。出于我的目的,我更喜欢第二种形式,因为它更有效地利用编译器来发现错误,但长度可能会有所不同。最重要的一点是在理解Python的代码级别处理Python错误。如果你的整个应用程序需要理解Python,你应该考虑用Python重写而不是嵌入,也许根据需要使用一些C++的模块。
与往常一样,您可以通过克隆github repo来完成本教程。