Boost.Python - Passing boost::python::object as argument to python function? - c++

So I'm working on a little project in which I'm using Python as an embedded scripting engine. So far I've not had much trouble with it using boost.python, but there's something I'd like to do with it if it's possible.
Basically, Python can be used to extend my C++ classes by adding functions and even data values to the class. I'd like to be able to have these persist in the C++ side, so one python function can add data members to a class, and then later the same instance passed to a different function will still have them. The goal here being to write a generic core engine in C++, and let users extend it in Python in any way they need without ever having to touch the C++.
So what I thought would work was that I would store a boost::python::object in the C++ class as a value self, and when calling the python from the C++, I'd send that python object through boost::python::ptr(), so that modifications on the python side would persist back to the C++ class. Unfortunately when I try this, I get the following error:
TypeError: No to_python (by-value) converter found for C++ type: boost::python::api::object
Is there any way of passing an object directly to a python function like that, or any other way I can go about this to achieve my desired result?
Thanks in advance for any help. :)

Got this fantastic solution from the c++sig mailing list.
Implement a std::map<std::string, boost::python::object> in the C++ class, then overload __getattr__() and __setattr__() to read from and write to that std::map. Then just send it to the python with boost::python::ptr() as usual, no need to keep an object around on the C++ side or send one to the python. It works perfectly.
Edit: I also found I had to override the __setattr__() function in a special way as it was breaking things I added with add_property(). Those things worked fine when getting them, since python checks a class's attributes before calling __getattr__(), but there's no such check with __setattr__(). It just calls it directly. So I had to make some changes to turn this into a full solution. Here's the full implementation of the solution:
First create a global variable:
boost::python::object PyMyModule_global;
Create a class as follows (with whatever other information you want to add to it):
class MyClass
{
public:
//Python checks the class attributes before it calls __getattr__ so we don't have to do anything special here.
boost::python::object Py_GetAttr(std::string str)
{
if(dict.find(str) == dict.end())
{
PyErr_SetString(PyExc_AttributeError, JFormat::format("MyClass instance has no attribute '{0}'", str).c_str());
throw boost::python::error_already_set();
}
return dict[str];
}
//However, with __setattr__, python doesn't do anything with the class attributes first, it just calls __setattr__.
//Which means anything that's been defined as a class attribute won't be modified here - including things set with
//add_property(), def_readwrite(), etc.
void Py_SetAttr(std::string str, boost::python::object val)
{
try
{
//First we check to see if the class has an attribute by this name.
boost::python::object obj = PyMyModule_global["MyClass"].attr(str.c_str());
//If so, we call the old cached __setattr__ function.
PyMyModule_global["MyClass"].attr("__setattr_old__")(ptr(this), str, val);
}
catch(boost::python::error_already_set &e)
{
//If it threw an exception, that means that there is no such attribute.
//Put it on the persistent dict.
PyErr_Clear();
dict[str] = val;
}
}
private:
std::map<std::string, boost::python::object> dict;
};
Then define the python module as follows, adding whatever other defs and properties you want:
BOOST_PYTHON_MODULE(MyModule)
{
boost::python::class_<MyClass>("MyClass", boost::python::no_init)
.def("__getattr__", &MyClass::Py_GetAttr)
.def("__setattr_new__", &MyClass::Py_SetAttr);
}
Then initialize python:
void PyInit()
{
//Initialize module
PyImport_AppendInittab( "MyModule", &initMyModule );
//Initialize Python
Py_Initialize();
//Grab __main__ and its globals
boost::python::object main = boost::python::import("__main__");
boost::python::object global = main.attr("__dict__");
//Import the module and grab its globals
boost::python::object PyMyModule = boost::python::import("MyModule");
global["MyModule"] = PyMyModule;
PyMyModule_global = PyMyModule.attr("__dict__");
//Overload MyClass's setattr, so that it will work with already defined attributes while persisting new ones
PyMyModule_global["MyClass"].attr("__setattr_old__") = PyMyModule_global["MyClass"].attr("__setattr__");
PyMyModule_global["MyClass"].attr("__setattr__") = PyMyModule_global["MyClass"].attr("__setattr_new__");
}
Once you've done all of this, you'll be able to persist changes to the instance made in python over to the C++. Anything that's defined in C++ as an attribute will be handled properly, and anything that's not will be appended to dict instead of the class's __dict__.

Related

Luabind objects disapearing when I enter a new file

I'm using Luabind to bind my Lua scripts to my C++ engine. (it uses lua 5.1.4)
I added a new lua script called "controller.lua" which my entities script, called "cat.lua", will reference and use. One the c++ calls the method "Update" it's all in the hands of Lua.
But once I try to pass my binded c++ methods to the new script file, it feels like all the bindings from that c++ object disapear. I get the following error:
Expression: scripts/controller.lua:5(method MoveUp)
scripts/controller.lua:5: attempt to call method 'GetComponent' (a nil value)
Here is some C++ snippets
// Definitions
module(luaState)
[
class_<Entity>("Entity")
.def("GetComponent", &Entity::GetComponent)
class_<Component>("Component")
.enum_("eComponentTypes")
[
value("Steering", kComponentType_Steering)
],
class_<SteeringComponent>("SteeringComponent")
];
// The script components update
void ScriptComponent::Update() {
const Entity* owner = this.GetOwner();
mLuaDataTable["Update"](owner); // Executes the Update function on the script Cat.lua
}
The entities code being called by c++ (When it executes it returns the Cat table to c++.)
-- Cat.lua
local controller = loadfile("scripts/controller.lua")
local Cat = {}
function Cat.Update(entity)
steeringComponent = entity:GetComponent(Component.Steering) -- Works fine
controller:MoveUp(entity)
end
return Cat
and the Controller
--controller.lua
local up = vec2(0.0, 1.0)
local Controller = {}
function Controller.MoveUp(entity)
steeringComponent = entity:GetComponent(Component.Steering) -- Fails
end
return Controller
Bonus points:
When I make a change to the controller that doesn't work (like if I just threw an s character anywhere), the controller loads up nil, no warnings. Is there some way to make it throw warnings?
Is there a better way I should be doing to "link" to other lua files, like the way im working with Controller?
Thanks to ToxicFrog on Freenode chat for helping me figure this one out.
Basically: I was calling controller MoveUp like so:
controller:MoveUp(entity)
which of course translates into
controller.MoveUp(controller, entity)
and the function was defined as
function Controller.MoveUp(entity)
this "entity" was accepted as the first parameter, controller, while the actual entity is discarded per spec.
http://lua-users.org/wiki/ObjectOrientationTutorial

libxml2 with default sax handler and custom error handler

I would like to use a simple libxml2 parser in a C++ program the following way:
default sax handler is fine (actually I'd like to avoid the effort of writing my own. I understand that I can do what I want with a custom sax handler)
the parser should be embedded in a C++ class that can be instantiated arbitrarily (possibly multi-threaded), the libxml2 parser context as member var
there are other components also using libxml2 but out of my control (I cannot
exactly tell what they do and how they use libxml2)
in the C++ class I want to use a custom error handler that does not just prints to stderr but I want to collect the errors and throw an exception
Example:
class XmlParser
{
public:
XmlDoc * parseText(const char * txt, ...);
private:
xmlParserCtxtPtr ctx;
static void xmlErrorHandler(void * userData, xmlErrorPtr err);
}
Here is what does NOT work (to my testing and understanding):
use xmlSetStructuredErrorFunc() or xmlSetGenericErrorFunc() and set the current C++ instance as user data because these funcs just set a global var (not thread-safe)
use xmlNewParserCtxt() and set ctx->sax->serror to a regular C++ method - error handler must be static
same as previous but with a static class method - actually that does work but at the same time I want to set ctx->user_data (to 'this' of the current C++ instance) - that makes the parser crash, it looks as if inside of libxml2 ctx->user_data is passed through the functions where there should be just ctx ... however that happens consistently, i.e. looks rather like a feature than a bug :-)
Now, has anybody an idea how to get this to work?
Many thx!!!

How can I extract a wrapped C++ type from a Python type using boost::python?

I've wrapped a C++ class using Py++ and everything is working great in Python. I can instantiate the c++ class, call methods, etc.
I'm now trying to embed some Python into a C++ application. This is also working fine for the most-part. I can call functions on a Python module, get return values, etc.
The python code I'm calling returns one of the classes that I wrapped:
import _myextension as myext
def run_script(arg):
my_cpp_class = myext.MyClass()
return my_cpp_class
I'm calling this function from C++ like this:
// ... excluding error checking, ref counting, etc. for brevity ...
PyObject *pModule, *pFunc, *pArgs, *pReturnValue;
Py_Initialize();
pModule = PyImport_Import(PyString_FromString("cpp_interface"));
pFunc = PyObject_GetAttrString(pModule, "run_script");
pArgs = PyTuple_New(1); PyTuple_SetItem(pArgs, 0, PyString_FromString("an arg"));
pReturnValue = PyObject_CallObject(pFunc, pArgs);
bp::extract< MyClass& > extractor(pReturnValue); // PROBLEM IS HERE
if (extractor.check()) { // This check is always false
MyClass& cls = extractor();
}
The problem is the extractor never actually extracts/converts the PyObject* to MyClass (i.e. extractor.check() is always false).
According to the docs this is the correct way to extract a wrapped C++ class.
I've tried returning basic data types (ints/floats/dicts) from the Python function and all of them are extracted properly.
Is there something I'm missing? Is there another way to get the data and cast to MyClass?
I found the error. I wasn't linking my bindings in my main executable because the bindings were compiled in a separate project that created the python extension only.
I assumed that by loading the extension using pModule = PyImport_Import(PyString_FromString("cpp_interface")); the bindings would be loaded as well, but this is not the case.
To fix the problem, I simply added the files that contain my boost::python bindings (for me, just wrapper.cpp) to my main project and re-built.

How to add a property to a module in boost::python?

You can add a property to a class using a getter and a setter (in a simplistic case):
class<X>("X")
.add_property("foo", &X::get_foo, &X::set_foo);
So then you can use it from python like this:
>>> x = mymodule.X()
>>> x.foo = 'aaa'
>>> x.foo
'aaa'
But how to add a property to a module itself (not a class)?
There is
scope().attr("globalAttr") = ??? something ???
and
def("globalAttr", ??? something ???);
I can add global functions and objects of my class using the above two ways, but can't seem to add properties the same way as in classes.
__getattr__ and __setattr__ aren't called on modules, so you can't do this in ordinary Python without hacks (like storing a class in the module dictionary). Given that, it's very unlikely there's an elegant way to do it in Boost Python either.
boost.python/HowTo on Python Wiki has an example of exposing C++ object as a module attribute inside BOOST_PYTHON_MODULE:
namespace bp = boost::python;
BOOST_PYTHON_MODULE(example)
{
bp::scope().attr("my_attr") = bp::object(bp::ptr(&my_cpp_object));
}
To set the attribute outside of BOOST_PYTHON_MODULE use
bp::import("example").attr("my_attr") = bp::object(bp::ptr(&my_cpp_object));
Now you can do in python something like
from example import my_attr
Of course, you need to register class of my_cpp_object in advance (e.g. you can do this inside the same BOOST_PYTHON_MODULE call) and ensure C++ object lifetime exceeds that of the python module. You can use any bp::object instead of wrapping C++ one.
Note that BOOST_PYTHON_MODULE swallows exceptions, so if you make a mistake, you don't receive any error indication and BOOST_PYTHON_MODULE-generated function will just immediately return. To ease debugging of this case you can catch exceptions inside BOOST_PYTHON_MODULE or temporary add some logging statement as a last line of BOOST_PYTHON_MODULE to see that it is reached:
BOOST_PYTHON_MODULE(example)
{
bp::scope().attr("my_attr2") = new int(1); // this will compile
std::cout << "init finished\n"; // OOPS, this line will not be reached
}

How does one get the instance of a Ruby class running in the current RB file? (Embedding Ruby in C++)

I have embedded Ruby inside my C++ application. I have generated the bindings using SWIG.
Basically, I run the ruby file and then Ruby takes over and calls my C++ class.
Based on my previous question, I would like to get the current instance of the class that is defined in the ruby file back to the C++ class so that I may execute instance methods.
I execute the ruby file as follows:
rb_eval_string_protect(<ruby script string>, &status );
rb_funcall(Qnil, rb_intern("main"), 0);
The global main method in the script creates an instance of the defined class in the file. That's the instance I am after.
If I have to, I will add a parameter or another function to pass the instance back, however, I'm not sure how to define that in C++ so that when SWIG generates the binding, it all works ok.
Any help would be appreciated.
Previous Question: Calling Ruby class methods from C++
The C api for ruby does its best to preserve ruby's functional nature, so rb_eval_string_protect() returns the VALUE of the last line of the script given, and rb_funcall() returns the VALUE of the last line of the method invoked.
So the trick is really to think of it as how would you get that instance value in pure ruby? If it's just the return value of main, like
# I'm a ruby script!
main_retval = main()
Then capturing the return value in C is similar:
// I'm some C (or C++) code
VALUE main_retval;
// ...
rb_eval_string_protect("...", &status);
main_retval = rb_funcall(Qnil, rb_intern("main"), 0);
And would give you a reference to the ruby object returned by main.
You can use this object as normal, calling methods and the like
VALUE main_retval_as_string = rb_funcall(main_retval, rb_intern("to_s"), 0);