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

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

本教程的第2部分中,我介绍了一种方法,使用应用程序的C++代码处理嵌入的Python代码抛出的异常。这对于调试嵌入式Python代码至关重要。在本教程中,我们将创建一个简单的C++类,它利用Python功能来处理开发实际应用程序中经常令人烦恼的部分:配置解析。

为了不让C++精英们感到愤怒,我将以外交方式说出这一点:我在C++中使用复杂的字符串操作。STL stringsstringstreams极大简化了任务,但执行应用程序级任务,并以健壮的方式执行它们,总是导致我编写更多的代码。因此,我最近使用嵌入Python,特别是ConfigParser模块,重新编写了Granola Connect(Granola Enterprise中用于处理与Granola REST API通信的守护进程)的配置解析机制。

当然,字符串操作和配置解析只是一个例子。对于第3部分,我可以选择任何数量的C++难以处理而Python中很简单的任务(例如,Web连接),但是配置解析类是一个简单但完整的用于嵌入Python以供实际使用的示例。从Github repo中获取本教程的代码。

首先,让我们创建一个涵盖非常基本的配置解析的类定义:读取和解析INI样式的文件,提取给定名称和节的字符串值,并为给定的节设置字符串值。这是类声明:

class ConfigParser{
    private:
        boost::python::object conf_parser_;

        void init();
    public:
        ConfigParser();

        bool parse_file(const std::string &filename);
        std::string get(const std::string &attr,
                        const std::string &section = "DEFAULT");
        void set(const std::string &attr,
                 const std::string &value,
                 const std::string &section = "DEFAULT");
};

ConfigParser模块提供的功能远远超出本教程所涵盖的功能,但我们在此实现的子集应作为实现更复杂功能的模板。该类的实现相当简单; 首先,构造函数加载__main__模块,提取字典,将ConfigParser模块导入命名空间,并创建一个boost::python::object类型的成员变量来包含RawConfigParser对象:

ConfigParser::ConfigParser(){
    py::object mm = py::import("__main__");
    py::object mn = mm.attr("__dict__");
    py::exec("import ConfigParser", mn);
    conf_parser_ = py::eval("ConfigParser.RawConfigParser()", mn);
}

用以下config_parser_对象执行文件解析以及值的获取和设置:

bool ConfigParser::parse_file(const std::string &filename){
    return py::len(conf_parser_.attr("read")(filename)) == 1;
}

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

void ConfigParser::set(const std::string &attr, const std::string &value, const std::string &section){
    conf_parser_.attr("set")(section, attr, value);
}

在这个简单的例子中,为了简洁起见,允许传播异常。在更复杂的环境中,您几乎肯定希望让C++类处理并将Python异常重新打包为C++异常。如果性能或其他问题成为问题,您可以稍后创建一个纯C++类。

要使用该类,调用代码可以简单地将其视为普通的C++类:

int main(){
    Py_Initialize();
    try{
        ConfigParser parser;
        parser.parse_file("conf_file.1.conf");
        cout << "Directory (file 1): " << parser.get("Directory", "DEFAULT") << endl;
        parser.parse_file("conf_file.2.conf");
        cout << "Directory (file 2): " << parser.get("Directory", "DEFAULT") << endl;
        cout << "Username: " << parser.get("Username", "Auth") << endl;
        cout << "Password: " << parser.get("Password", "Auth") << endl;
        parser.set("Directory", "values can be arbitrary strings", "DEFAULT");
        cout << "Directory (force set by application): " << parser.get("Directory") << endl;
        // Will raise a NoOption exception 
        // cout << "Proxy host: " << parser.get("ProxyHost", "Network") << endl;
    }catch(boost::python::error_already_set const &){
        string perror_str = parse_python_exception();
        cout << "Error during configuration parsing: " << perror_str << endl;
    }
}

就是这样:一个包含块和注释的键值配置解析器只需要50行代码。这只是冰山一角。在几乎相同长度的代码中,您可以执行各种各样的事情,这些事情在C++中最为痛苦,更容易出错且耗时:配置解析、列表和集合操作、Web连接、文件格式操作(想想XML/JSON),以及无数其他已在Python标准库中实现的任务。

第4部分中,我将介绍如何使用仿函数和Python命名空间的类来更强大和通用地调用Python代码。

猜你喜欢

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