使用Boost::Python在C++应用程序中嵌入Python:第四部分

翻译: Leon Lee([email protected])
原文:在此

在这个教程的第2部分中,我介绍了用于从C++解析Python异常的代码。在第3部分中,我使用Python ConfigParser模块实现了一个简单的配置解析类。作为该实现的一部分,我提到对于任何规模的项目,人们都希望在类中捕获并处理Python异常,以便该类的客户不必了解Python的细节。从调用者的角度来看,这个类就像任何其他C++类一样。

处理Python异常的明显方法是在每个函数中处理它们。例如,我们创建的C++ ConfigParser类的get函数将变为:

std::string ConfigParser::get(const std::string &attr, const std::string &section){
    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::bindboost::functionboost::function提供仿函数类包装器,boost::bind绑定函数的参数。然后,这两者一起启用函数及其参数的传递,这些函数及其参数可以在以后调用。正是医生所要求的!

要使用仿函数,函数需要知道返回类型。由于我们使用不同的签名包装函数,因此函数模板可以很好地完成这一操作:

template <class return_type>
return_type call_python_func(boost::function<return_type ()> 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 &section){
    return call_python_func<string>(boost::bind(&ConfigParser::get_py, this, attr, section),
                                    "Error getting configuration option: ");
}

string ConfigParser::get_py(const string &attr, const string &section){
    return py::extract<string>(conf_parser_.attr("get")(section, attr));
}

get函数将传入的参数与隐式this指针绑定到get_py函数,get_py·函数又调用boost::python`执行操作所需的函数。简单有效。

当然,这里有一个权衡。不是重复的try...catch块代码和Python错误处理,每个类声明的函数数量增加了一倍。出于我的目的,我更喜欢第二种形式,因为它更有效地利用编译器来发现错误,但长度可能会有所不同。最重要的一点是在理解Python的代码级别处理Python错误。如果你的整个应用程序需要理解Python,你应该考虑用Python重写而不是嵌入,也许根据需要使用一些C++的模块。

与往常一样,您可以通过克隆github repo来完成本教程。

猜你喜欢

转载自blog.csdn.net/cfy_focusoncdr/article/details/82832008