I'm trying to use Python as a scripting language for a C++ project of mine, so I reached for Boost.Python to make the task less tedious. I have a C++ class (ScriptSystem) which is responsible for holding references to and executing Python scripts.
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;
};
The Python scripts are supposed to be subclasses of a wrapped C++ interface named IScript.
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;
}
};
I managed to expose the ScriptSystem class and the IScript interface to Python, and it almost sorta kinda works. I can create new instances of IScript and pass them to ScriptSystem without any trouble. I can create subclasses which override tick(self)
and as long as they don't define a constructor I'm good to go.
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*>();
}
I can add data attributes to the class, but adding __init__(self)
causes problems. For example, this code snippet executes just fine:
(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)
But this code fails:
(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*)
So, I guess my question is: What is happening here?! I don't understand why adding a constructor to a Python subclass of Script (the python wrapper for IScript) causes an argument mismatch error.