Advertisement

Boost.Python loading from script

Started by February 13, 2012 02:57 AM
0 comments, last by Matt328 12 years, 9 months ago
I'm investigating using Boost.Python for scripting in a game. After reading about all the documentation I can find on embedding Python using Boost.Python, I think a good strategy to pursue would to be have base classes defined in C++, and be able to create derived classes in Python scripts, and then call the base class methods on objects from my game, which would defer to the virtual methods defined in a Python script to execute some logic.

Given the following code, the next step I would like to try to take would be to have the exec call be replaced with the exec_file call, but apparently there is alot I don't understand yet.

[source]#include "stdafx.h"

namespace py = boost::python;

class Object {
public:
Object(int id) :
id(id) {
}
virtual ~Object() {
}

int getId() const {
return id;
}

virtual int f() {
std::cout << "Object" << std::endl;
return 0;
}

private:
int id;
};

struct Derived: Object {
Derived(int i) :
Object(i) {
}

virtual int f(void) {
std::cout << "Derived\n";
return 0;
}
};

struct ObjectWrap: public Object, public py::wrapper<object> {
ObjectWrap(int i) :
Object(i) {
}

int f() {
if (py::override f = this->get_override("f")) {
return f();
}
return Object::f();
}

int default_f() {
return this->Object::f();
}
};

BOOST_PYTHON_MODULE(Core) {
py::class_<object, boost::shared_ptr<object="">, boost::noncopyable>("__Object", "I'm an implementation detail, Pretend I don't exist", py::no_init)
.add_property("id", &Object::getId)
.def("f", &Object::f);

py::class_<objectwrap, boost::shared_ptr<objectwrap="">, boost::noncopyable>("Object", py::init<int>())
.add_property("id", &Object::getId)
.def("f", &Object::f, &ObjectWrap::default_f);
}

int _tmain(int argc, _TCHAR* argv[]) {
Py_Initialize();
try {
initCore();

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

py::object ignored3 = py::exec_file("CustomObject.py", main_namespace,
main_namespace);

py::object ignored = py::exec("import Core\n"
"class CustomObject(Core.Object):\n"
" def __init__(self,id):\n"
" Core.Object.__init__(self,id)\n"
" def f(self):\n"
" print \"CustomObject\"\n"
" return 0\n"
"object = CustomObject(1337)\n", main_namespace, main_namespace);

py::object object = main_namespace["object"];
object.attr("f")();

boost::shared_ptr<object> o = py::extract<boost::shared_ptr<object>>(
object);
o->f();

boost::shared_ptr<object> o2(new Derived(1337));
main_namespace["object"] = o2;
py::object ignored2 = py::exec("object.f()\n", main_namespace,
main_namespace);

} catch (const py::error_already_set&) {
PyErr_Print();
}

Py_Finalize();
system("pause");
return 0;
}[/source]

When I uncomment the line with the exec_file call, and comment out the line with the exec call, I get the following error:

Traceback (most recent call last):
  File "CustomObject.py", line 10, in <module>
	object = CustomObject(1337)
  File "CustomObject.py", line 5, in __init__
	Core.Object.__init__self(id)
AttributeError: type object 'Object' has no attribute '_CustomObject__init__self'


I am guessing this is happening because my base class, Object is not able to be imported into the main namespace, being that it is defined on the C++ side? Or not, I really have no clue and am just stabbing in the dark here.

The code, as posted, works just fine, but isn't all that useful to me since it's executing a Python string defined in C++. I could read the .py files into a string, and use the exec call to execute them that way, but the exec_file seems much more elegant. Unfortunately, all the examples end at this point, and leave incorporating reading actual Python scripts as an exercise for the reader.

Can anyone point me in the right direction, and at least verify that my approach is valid?

Edit: There is definitely something wrong with the source tags, putting lang="cpp" in there caused it to seem to choke on the double less than symbol. At least all of the code is there now. If a mod wants to fix it or enlighten me on the subtleties of the new forum software, I'd appreciate it.
I have solved the problem, if you look at the error I posted, the line where I try to call [color=#282828]

Core.Object.__init__self(id) should read

[color=#282828]

Core.Object.__init__(self,id)



[color=#282828]

With that, I'm moving on for now, but still wondering if my approach here is valid, or if there is a better way. I'm thinking a possible strategy for controlling entities would be to have base classes defined in C++, for example a Controller class that receives events from an event system, and provides virtual methods for handling those events. I could then have classes defined in Python scripts that would inherit from this Controller class, and implement logic for different controllers in the Python scripts. It may also be possible to change a Controller's behavior at runtime by associating a different Python script with it to change it's behavior, or change an entity's controller altogether.



[color=#282828]

I'm still in the investigation stages of incorporating scripting, so some of that may be way off, but I do know it gets old incrementing a value by 10, recompiling and relaunching my program only to see it not have any effect, then rinsing and repeating a hundred times, so a scripting console is definitely a feature I'm after here as well.

This topic is closed to new replies.

Advertisement