使用嵌入式Python调用对象实例的函数

时间:2018-08-30 08:00:24

标签: python c++ c++11 boost

我希望能够在我的应用程序中运行Python脚本,以允许自动化操作并修改现有对象/现有对象的调用方法。

在我的应用程序中,有一个BasicWindow类和一个MainWindow类,它们是从前者派生的。现在,在应用程序启动时,我初始化MainWindow的一个实例。该对象具有许多功能,其中有一个可以加载文件(LoadFile()),这里我将以它为例。

让我们说我想调用那个特定对象实例的那个特定函数(但不限于那个函数,它只是我想从Python实现的一个例子)。

此方法不是静态方法。为此,我使用了 Boost.Python ,并通过以下方式创建了一个模块:

BOOST_PYTHON_MODULE(MyModule)
{

    MainWindow::PythonExpose(); //not really sure how to operate here
    //more stuff
}

我的想法是我可以从Python调用类似的东西

MainWindow.LoadFile()

甚至更好,只是:

LoadFile()

一种解决方案是创建静态的,应用程序范围内的函数,然后仅公开这些函数。在C ++中,我可以找到MainWindow的特定实例:(这两种方法都是静态的)

void AppHelper::LoadFile()
{
    GetMainWindow()->LoadFile();
}


void AppHelper::PythonExposeGlobal()
{
    using namespace boost::python;
    def("LoadFile", &AppHelper::LoadFile);
}

有可能实现这一目标吗?一般的问题是:是否可以从Python调用现有对象的方法(在C ++中)?如果是这样,该怎么办?如果没有,我该怎么做才能模仿这种行为?

例如,我可以轻松地在C#应用程序中启用脚本功能并共享现有对象的实例。 (但是C#当然具有反射性。)

1 个答案:

答案 0 :(得分:1)

如果您可以保证该对象能够在使用该脚本的任何脚本运行的情况下生存,那么我将使用一种相当简单的方法。

我将使用原始计数器类进行演示:

class counter
{
public:
    counter() : count(0) {}

    void increment() { ++count; }

    int count;
};

现在,我将此类公开给python,以使其认为它是不可复制的,并且不允许构造新实例。我还公开了我想从脚本中使用的所有成员。

BOOST_PYTHON_MODULE(example)
{
    bp::class_<counter, boost::noncopyable>("Counter", bp::no_init)
        .def("increment", &counter::increment)
        ;
}

下一步是创建一个使用现有实例的Python对象,并允许脚本使用它(例如,将其添加为某些模块的属性,例如主模块)。

counter c;

bp::object main_module(bp::import("__main__"));

main_module.attr("c") = bp::object(bp::ptr(&c));

现在您的脚本可以使用此实例:

c.increment()

示例程序:

#include <boost/python.hpp>
#include <iostream>

namespace bp = boost::python;

// Simple counter that can be incremented
class counter
{
public:
    counter() : count(0) {}

    void increment() { ++count; }

    int count;
};

// Expose the counter class to Python
// We don't need constructor, since we only intend to use instance
// already existing on the C++ side
BOOST_PYTHON_MODULE(example)
{
    bp::class_<counter, boost::noncopyable>("Counter", bp::no_init)
        .def("increment", &counter::increment)
        ;
}

int main()
{
    Py_InitializeEx(0);

    // Bind our class
    initexample();

    counter c;

    bp::object main_module(bp::import("__main__"));
    bp::object main_namespace(main_module.attr("__dict__"));

    // Add the current instance of counter to Python as attribute c of the main module
    main_module.attr("c") = bp::object(bp::ptr(&c));

    std::cout << "Before: " <<  c.count << '\n';

    // Increment the counter from Python side
    bp::exec("c.increment()", main_namespace);

    std::cout << "After: " << c.count << '\n';

    Py_Finalize();
    return 0;
}

控制台输出:

Before: 0
After: 1