如何使用boost.python公开对齐的类

时间:2012-11-01 12:56:41

标签: boost-python memory-alignment

尝试公开这样的对齐类时:

class __declspec(align(16)) foo
{
public:
    void foo_method() {}
};

BOOST_PYTHON_MODULE(foo_module)
{
    class_<foo>("foo")
        .def("foo_method", &foo::foo_method);
}

您最终会遇到错误(msvs 2010):

error C2719: 'unnamed-parameter': formal parameter with __declspec(align('16')) won't be aligned,
see reference to class template instantiation 'boost::python::converter::as_to_python_function<T,ToPython>' being compiled

到目前为止我找到的解决方案是使用智能指针来存储对象:

BOOST_PYTHON_MODULE(foo_module)
{
    class_<foo, boost::shared_ptr<foo>, boost::noncopyable>("foo")
        .def("foo_method", &foo::foo_method);
}

难道没有更好的解决方案吗?这非常烦人,因为你应该按值返回所有函数返回对象,以返回智能指针,性能也会下降。

1 个答案:

答案 0 :(得分:1)

我遇到了同样的问题,想要一个不涉及shared_ptr的解决方案。它涉及专门化一些boost :: python类,以确保我们得到一个足够大的存储区域,以便能够在其中对齐我们的对象。

我写了一篇很长的博客文章,解释了我是如何得出这个解决方案here的。以下是我找到的解决方案。我觉得这是一个非常黑客,所以它可能会破坏其他东西。但到目前为止它似乎工作,我没有找到更好的东西。

我试图暴露一个Eigen :: Quaternionf(需要16个字节对齐):

bp::class_<Quaternionf>("Quaternion", bp::init<float, float, float, float>())
  .def(bp::init<Matrix3f>())
  .add_property("w", get_prop_const(&Quaternionf::w))
  .add_property("x", get_prop_const(&Quaternionf::x))
  .add_property("y", get_prop_const(&Quaternionf::y))
  .add_property("z", get_prop_const(&Quaternionf::z))
  .def("matrix", &Quaternionf::matrix)
  .def("rotvec", &quaternion_to_rotvec);

解决方案涉及专门化3个类:

boost :: python :: objects :: instance 比我们的类型要求多16个字节,以确保我们可以对齐

...
union
{
    align_t align;
    char bytes[sizeof(Data) + 16];
} storage;
...

boost :: python :: objects :: make_instance_impl 正确设置实例的Py_SIZE

...
Holder* holder = Derived::construct(
  &instance->storage, (PyObject*)instance, x);
holder->install(raw_result);

// Note the position of the internally-stored Holder,
// for the sake of destruction
// Since the holder not necessarily allocated at the start of
// storage (to respect alignment), we have to add the holder
// offset relative to storage
size_t holder_offset = reinterpret_cast<size_t>(holder)
                        - reinterpret_cast<size_t>(&instance->storage)
                        + offsetof(instance_t, storage);
Py_SIZE(instance) = holder_offset;
...

boost :: python :: objects :: make_instance ,以便构造方法将持有者对齐存储

...
static inline QuaternionfHolder* construct(void* storage, PyObject* instance, reference_wrapper<Quaternionf const> x)
{
  // From the specialized make_instance_impl above, we are guaranteed to
  // be able to align our storage
  void* aligned_storage = reinterpret_cast<void*>(
    (reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16);
  QuaternionfHolder* new_holder = new (aligned_storage) 
    QuaternionfHolder(instance, x);
  return new_holder;
}
...

完整代码如下:

typedef bp::objects::value_holder<Eigen::Quaternionf> QuaternionfHolder;

namespace boost { namespace python { namespace objects {

using namespace Eigen;

//template <class Data = char>
template<>
struct instance<QuaternionfHolder>
{
  typedef QuaternionfHolder Data;
    PyObject_VAR_HEAD
    PyObject* dict;
    PyObject* weakrefs;
    instance_holder* objects;

    typedef typename type_with_alignment<
        ::boost::alignment_of<Data>::value
    >::type align_t;

    union
    {
        align_t align;
        char bytes[sizeof(Data) + 16];
    } storage;
};


// Adapted from boost/python/object/make_instance.hpp

//template <class T, class Holder, class Derived>
template<class Derived>
struct make_instance_impl<Quaternionf, QuaternionfHolder, Derived>
{
    typedef Quaternionf T;
    typedef QuaternionfHolder Holder;

    typedef objects::instance<Holder> instance_t;

    template <class Arg>
    static inline PyObject* execute(Arg& x)
    {
        BOOST_MPL_ASSERT((mpl::or_<is_class<T>, is_union<T> >));

        PyTypeObject* type = Derived::get_class_object(x);

        if (type == 0)
            return python::detail::none();

        PyObject* raw_result = type->tp_alloc(
            type, objects::additional_instance_size<Holder>::value);

        if (raw_result != 0)
        {
            python::detail::decref_guard protect(raw_result);

            instance_t* instance = (instance_t*)raw_result;

            // construct the new C++ object and install the pointer
            // in the Python object.
            //Derived::construct(&instance->storage, (PyObject*)instance, x)->install(raw_result);
            Holder* holder = Derived::construct(
                &instance->storage, (PyObject*)instance, x);
            holder->install(raw_result);

            // Note the position of the internally-stored Holder,
            // for the sake of destruction
            // Since the holder not necessarily allocated at the start of
            // storage (to respect alignment), we have to add the holder
            // offset relative to storage
            size_t holder_offset = reinterpret_cast<size_t>(holder)
                                 - reinterpret_cast<size_t>(&instance->storage)
                                 + offsetof(instance_t, storage);
            Py_SIZE(instance) = holder_offset;

            // Release ownership of the python object
            protect.cancel();
        }
        return raw_result;
    }
};


//template <class T, class Holder>
template<>
struct make_instance<Quaternionf, QuaternionfHolder>
    : make_instance_impl<Quaternionf, QuaternionfHolder, make_instance<Quaternionf,QuaternionfHolder> >
{
    template <class U>
    static inline PyTypeObject* get_class_object(U&)
    {
        return converter::registered<Quaternionf>::converters.get_class_object();
    }

    static inline QuaternionfHolder* construct(void* storage, PyObject* instance, reference_wrapper<Quaternionf const> x)
    {
      LOG(INFO) << "Into make_instance";
      LOG(INFO) << "storage : " << storage;
      LOG(INFO) << "&x : " << x.get_pointer();
      LOG(INFO) << "&x alignment (0 = aligned): " << (reinterpret_cast<size_t>(x.get_pointer()) & 0xf);

      // From the specialized make_instance_impl above, we are guaranteed to
      // be able to align our storage
      void* aligned_storage = reinterpret_cast<void*>(
          (reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16);
      QuaternionfHolder* new_holder = new (aligned_storage) QuaternionfHolder(instance, x);
      LOG(INFO) << "&new_holder : " << new_holder;
      return new_holder;
      //return new (storage) QuaternionfHolder(instance, x);
    }
};


}}} // namespace boost::python::objects