How to override __setattr__ in a wrapped class (from C++)?

元气小坏坏 提交于 2019-12-13 16:27:21

问题


Using boost::python, I have been able to wrap a class (Node) which has some virtual functions, and it's been a hoot, but now I'm trying to override setattr/getattr for the class. I've got boost::python to call my own setattr implementation, but I can't figure out how to avoid the recursion that happens.

So I have a Node class, and I want to be able to write:

node1.param1 = 5          # Call node.SetParam
node1.plain_member = 7    # Call object.__setattr__

So far, I have (very abridged):

namespace bpy = boost::python;

struct NodeWrap : vcNode, bpy::wrapper<Node> {
  ...  
  static void setattr(bpy::object obj, std::string attr, bpy::object val)
  {
     NodeWrap const& node = bpy::extract<NodeWrap const&>(obj)();
     ParamRef p = node.FindParam(attr);
     if (p)
        py_to_param(p, val); //This part works fine
     else
     {
        obj.attr(attr) = val; //Problematic line - recurses indefinitely
     }
  }
};

then in the BOOST_PYTHON_MODULE section, I have:

bpy::class_<NodeWrap, boost::shared_ptr<NodeWrap>, boost::noncopyable>("Node")
  ...
  .def("__setattr__". &NodeWrap::setattr);

As I said, it compiles just fine, and will set any node "params" correctly, but if I try to set a non-param, such as self.plain_old_member = 7 then I get a "Recursion Depth Exceeded" error, because I gather boost::object::attr will call the class' __setattr__ method

In python, I would get around this by using

super(Node).__setattr__(self, attr, val)

in the problem line, but I can't figure out how to do that from boost::python. I don't mind going to the C API if I have to (in fact, I've tried using PyObject_SetAttrString to no avail), but I just don't know how to get there from here.

Cheers, Matt


回答1:


I believe the Python C-API function you want is PyObject_GenericSetAttr; that is, you should replace the "problematic line" with:

bpy::str attr_str(attr);
if (PyObject_GenericSetAttr(obj.ptr(), attr_str.ptr(), val.ptr()) != 0)
    bpy::throw_error_already_set();

I think this is equivalent to calling object.__setattr__(self, attr, val) in Python, which is not the same as calling __setattr__ on a base class, but the difference only matters if a base class also overrides __setattr__. If that's the case, you'd need to do something like this:

bpy::object cls(bpy::handle<>(PyObject_Type(obj.ptr())));
bpy::object base_cls = cls.attr("__bases__")[0];
base_cls.attr("__setattr__")(obj, attr, val);


来源:https://stackoverflow.com/questions/10694367/how-to-override-setattr-in-a-wrapped-class-from-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!