2013-07-24 1 views
3

Вот вопрос boost.python, на который я не мог ответить, используя документацию.обход реестра to_python для класса, экспортированного с помощью класса _ <>?

У меня есть два класса C++ A и B, такие, что B является подклассом A, и при определенных условиях объект типа A может быть преобразован в объект типа B. Оба эти класса экспортируются с классом_ <>. Схематично:

class A 
{ 
public: 
    bool is_B() const { ... } // returns true if conversion to B will succeed 
    ... 
}; 

class B : public A { 
    B(const A &a) { ... } // should only be called if a.is_B() == true 
    ... 
}; 

class_<A>("A") ... ; 
class_<B,bases<A> >("B") ... ; 

Я хотел бы конвертер к-питона для класса А, чтобы вернуть объект класса B, если это возможно; в противном случае он должен возвращать объект класса А. Схематически:

struct A_to_python_converter { 
    PyObject *convert(const A &a) { 
     if (a.is_B()) 
      // return object of class B 
      return boost::python::incref(boost::python::object(B(a)).ptr()); 
     else 
      ... // return object of class A 
    } 
} 

boost::python::to_python_converter<A,A_to_python_converter>(); 

Проблема заключается в том, что я не уверен, что код поставить на место «...» выше. Если я ставлю:

// return object of class A 
return boost::python::incref(boost::python::object(a).ptr()); 

тогда я получаю бесконечный цикл, так A_to_python_converter :: обращенного() будет вызываться рекурсивно. Есть ли способ обойти реестр и преобразовать объект класса A в (PyObject *) без прохождения A_to_python_converter, учитывая, что A уже экспортирован с классом <>? Это решило бы мою проблему чисто (хотя я открыт для других предложений).

Спасибо! K

ответ

2

Ожидается, что тип будет иметь однократное преобразование на Python, зарегистрированное на нем.

Если класс открыт через boost::python::class_, введите информацию и зарегистрируйтесь. Кроме того, если boost::noncopyable не предоставляется, то регистрируются преобразователи на Python и from-Python, которые будут копировать T по значению. Пользователи могут регистрировать свои собственные преобразователи, используя boost::python::to_python_converter.

Следовательно, одно решение для подавления преобразователей по умолчанию для класса A с помощью boost::noncopyable, затем зарегистрировать собственный конвертер, который будет создавать объекты Python, которые крепят либо экземпляры A или экземпляров B. Этот подход будет использовать API более низкого уровня в Boost.Python для обработки создания экземпляра.

/// @brief Custom converter that converts A to either an A or B Python object. 
struct class_A_cref_wrapper 
    : boost::python::to_python_converter<A, class_A_cref_wrapper> 
{ 
    // Type that makes instances that hold A by value. 
    typedef boost::python::objects::make_instance<A, 
     boost::python::objects::value_holder<A> 
    > instance_maker; 

    static PyObject* convert(const A& a) 
    { 
    namespace python = boost::python; 
    return a.is_B() 
     ? python::incref(python::object(B(a)).ptr()) // Create B. 
     : instance_maker::execute(boost::ref(a)); // Create A.  
    } 
}; 

Вот полный пример demonstrating такой подход:

#include <boost/python.hpp> 

// Legacy API. 
class A 
{ 
public: 
    A() : b_(false) {} 
    A(bool b) : b_(b) {} 
    bool is_B() const { return b_; } // true if conversion to B will succeed 
private: 
    bool b_; 
}; 

class B: public A 
{ 
public: 
    B() : A()   {} 
    B(const A& a) : A(a) {} 
}; 

/// @brief Factory functions that return an A type with is_B of false. 
A make_A() { return A(false); } 

/// @brief Factory functions that return an A type with is_B of true. 
A make_B() { return A(true); } 

/// @brief Custom converter that converts A to either an A or B Python object. 
struct class_A_cref_wrapper 
    : boost::python::to_python_converter<A, class_A_cref_wrapper> 
{ 
    // Make and hold instances by value. 
    typedef boost::python::objects::make_instance<A, 
     boost::python::objects::value_holder<A> 
    > instance_maker; 

    static PyObject* convert(const A& a) 
    { 
    namespace python = boost::python; 
    return a.is_B() 
     ? python::incref(python::object(B(a)).ptr()) // Create B. 
     : instance_maker::execute(boost::ref(a)); // Create A.  
    } 
}; 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 

    // Expose A and B classes. Use boost::noncopyable to suppress to-Python 
    // and from-Python converter regristration for class A. 
    python::class_<A, boost::noncopyable>("A"); 
    python::class_<B, python::bases<A> >("B"); 

    // Register a custom converter for A. 
    class_A_cref_wrapper(); 

    // Expose factory functions that always return an A type. This will 
    // cause to_python converters to be invoked when invoked from Python. 
    python::def("make_A", &make_A); 
    python::def("make_B", &make_B); 
} 

Интерактивное использование:

>>> import example 
>>> assert(isinstance(example.make_A(), example.A)) 
>>> assert(isinstance(example.make_B(), example.B)) 
>>> assert(isinstance(example.make_B(), example.A)) 
>>> assert(not isinstance(example.make_A(), example.B)) 
Смежные вопросы