我尝试使用Python作为我的C ++项目的脚本语言,所以我选择Boost.Python来减少任务的繁琐。我有一个C ++类(ScriptSystem),它负责保存对Python脚本的引用和执行。
namespace py = boost::python;
class ScriptSystem : public System
{
public:
virtual void execute_scripts() override;
void addToPyGlobals(const OtherSystemsPtr&);
void addScript(IScript*);
private:
std::vector<IScript *> scripts;
py::dict python_globals;
};
Python脚本应该是名为IScript的包装C ++接口的子类。
class IScript
{
public:
virtual ~IScript() {}
// This function is meant to be overridden. It prints
// to the console for debugging purposes only.
virtual void tick() const {
cout << "Default Implementation." << endl;
}
};
我设法将ScriptSystem类和IScript接口暴露给Python,它几乎有点有用。我可以创建新的IScript实例并将它们传递给ScriptSystem而不会有任何麻烦。我可以创建覆盖tick(self)
的子类,只要他们不定义构造函数我就可以了。
namespace py = boost::python;
class ScriptWrapper : public Script::IScript, public py::wrapper<Script::IScript>
{
public:
void tick()const override
{
// Check for Python override
if(py::override f = this->get_override("tick")) {
std::cout << "Found Python override"
<< std::endl;
f();
return;
}
// If there is no override, call the default implementation
std::cout << "No Python override found, calling C++ implementation."
<< std::endl;
this->Script::IScript::tick();
return;
}
void default_tick() const {
return this->Script::IScript::tick();
}
};
BOOST_PYTHON_MODULE(_script)
{
py::class_<ScriptSystem, boost::noncopyable>("ScriptSystem")
.def("add_script", &ScriptSystem::addScript);
py::class_<ScriptWrapper, boost::noncopyable>("Script")
.def("tick", &Script::IScript::tick, &ScriptWrapper::default_tick);
py::implicitly_convertible<ScriptWrapper*, Script::IScript*>();
}
我可以向类添加数据属性,但添加__init__(self)
会导致问题。例如,此代码段执行得很好:
(RemoteShell)
>>> from script import Script
>>> class One(Script):
... def tick(self):
... print "I'm a script!"
... print "My value is {}".format(One.value)
...
>>> One.value = 5
>>> one = One()
>>> script_system.add_script(one)
但是这段代码失败了:
(RemoteShell)
>>> from script import Script
>>> class Two(Script):
... def __init__(self):
... self.count = 0
... def tick(self):
... if self.count % 2 == 0:
... print "Tick"
... else:
... print "Tock"
... self.count += 1
...
>>> two = Two()
>>> script_system.add_script(two)
Traceback (most recent call last):
File "<console>", line 1, in <module>
ArgumentError: Python argument types in
ScriptSystem.add_script(ScriptSystem, Two)
did not match C++ signature:
add_script(ScriptSystem {lvalue}, Script::IScript*)
所以,我想我的问题是:这里发生了什么?!我不明白为什么将一个构造函数添加到Script的Python子类(IScript的python包装器)会导致参数不匹配错误。
答案 0 :(得分:2)
当派生类提供__init__
方法时,它必须显式调用其基类__init__
方法,以确保正确初始化实例的基类部分。在这种情况下,Script
实例的Two
部分未正确初始化,导致Boost.Python基于C ++类型进行失败调度。
要解决此问题,请考虑更改:
class Two(Script):
def __init__(self):
self.count = 0
为:
class Two(Script):
def __init__(self):
self.count = 0
Script.__init__(self)
这是一个完整的最小例子:
#include <boost/python.hpp>
/// @brief Mockup type.
class spam {};
/// @brief Mockup function that takes spam types.
void action(spam*) {}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<spam>("Spam");
python::def("action", &action);
}
交互式使用:
>>> import example
>>> example.action(example.Spam())
>>> class MoreSpam(example.Spam):
... def __init__(self):
... pass
...
>>> example.action(MoreSpam())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
example.action(MoreSpam)
did not match C++ signature:
action(spam*)
>>> class EvenMoreSpam(example.Spam):
... def __init__(self):
... example.Spam.__init__(self)
...
>>> example.action(EvenMoreSpam())