2016-04-07 2 views
4

Есть ли способ, чтобы обернуть boost::optional<T> тип объекта, чтобы разоблачить его с помощью boost::python::class_ (используется от BOOST_PYTHON_MODULE)обертка повышение :: опциональный помощью наддува :: питона

struct Foo 
{ 
    boost::optional<int> bar; 
}; 

BOOST_PYTHON_MODULE(module_name) 
{ 
    class_<Foo>("Foo") 
    .def_readwrite("bar", &Foo::bar); 
} 

Что я ожидаю в Python является AttributeError в этом случае

import module_name 
f = module_name.Foo() 
print f.bar 

как значение bar еще не был настроен. И TypeError когда

import module_name 
f = module_name.Foo() 
f.bar = "string" 

bar имеет int типа.

Другая связанная с этим проблема заключается в том, чтобы экспортировать объекты классов boost::python::indexing_suite типов контейнеров.

Является ли проблема разрешимой, используя boost::python api?

+0

См [этот вопрос] (http://stackoverflow.com/questions/26497922/how-to-wrap-ac -функции-то возвраты-boostoptionalt). Возможно, если он должен быть функцией для работы, а затем оберните его с помощью setter/getter. –

ответ

4

Вам нужны exception translator и python converters.

Exception переводчик

namespace bp = boost::python; 

// Custom exceptions 
struct AttributeError: std::exception 
{ 
    const char* what() const throw() { return "AttributeError exception"; } 
}; 

struct TypeError: std::exception 
{ 
    const char* what() const throw() { return "TypeError exception"; } 
}; 

// Set python exceptions 
void translate(const std::exception& e) 
{ 
    if(dynamic_cast<const AttributeError*>(&e)) 
    PyErr_SetString(PyExc_AttributeError, e.what()); 
    if(dynamic_cast<const TypeError*>(&e)) 
    PyErr_SetString(PyExc_TypeError, e.what()); 
} 

BOOST_PYTHON_MODULE(module_name) 
{ 
    // Exception translator 
    bp::register_exception_translator<AttributeError>(&translate); 
    bp::register_exception_translator<TypeError>(&translate); 
    ... 
} 

To-питона преобразователя

template <typename T> 
struct to_python_optional 
{ 
    static PyObject* convert(const boost::optional<T>& obj) 
    { 
    if(obj) return bp::incref(bp::object(*obj).ptr()); 
    // raise AttributeError if any value hasn't been set yet 
    else throw AttributeError(); 
    } 
}; 

BOOST_PYTHON_MODULE(module_name) 
{ 
    ... 
    bp::to_python_converter<boost::optional<int>, 
          to_python_optional<int> >(); 
    ... 
} 

Из-питон преобразователя

template<typename T> 
struct from_python_optional 
{ 
    static void* convertible(PyObject *obj_ptr) 
    { 
     try { return typename bp::extract<T>::extract(obj_ptr) ? obj_ptr : 0 ; } 
     // Without try catch it still raises a TypeError exception 
     // But this enables to custom your error message 
     catch(...) { throw TypeError(); } 
    } 

    static void construct(
    PyObject *obj_ptr, 
    boost::python::converter::rvalue_from_python_stage1_data* data) 
    { 
     const T value = typename bp::extract<T>::extract(obj_ptr); 

     assert(value); 

     void* storage = (
     (bp::converter::rvalue_from_python_storage<boost::optional<T> >*) 
     data)->storage.bytes; 

     new (storage) boost::optional<T>(value); 

     data->convertible = storage; 
    } 

    from_python_optional() 
    { 
    bp::converter::registry::push_back(
     &convertible, 
     &construct, 
     bp::type_id<boost::optional<T> >()); 
    } 
}; 

BOOST_PYTHON_MODULE(module_name) 
{ 
    ... 
    from_python_optional<int>(); 
    ... 
} 

Кроме того, вы не можете использовать преобразователи с def_readwrite(see this FAQ), вы должны использовать add_property.

BOOST_PYTHON_MODULE(module_name) 
{ 
    ... 
    bp::class_<Foo>("Foo") 
    .add_property("bar", bp::make_getter(&Foo::bar, 
         bp::return_value_policy<bp::return_by_value>()), 
         bp::make_setter(&Foo::bar, 
         bp::return_value_policy<bp::return_by_value>())); 
} 

Таким образом, вы получите эти выходные в интерпретатора Python:

>>> import module_name 
>>> f = module_name.Foo() 
>>> print f.bar 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: AttributeError exception 
>>> f.bar="string" 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: TypeError exception 
+0

Спасибо! Является ли аргумент указателя 'data' в' construct' функцией указателем на память, где фактически хранится переменная-член Foo :: bar? Если да, то почему нет проверки, что он еще не используется до 'new'? Другой вопрос о 'return_value_policy': 1. Почему мы указываем какую-либо политику возврата для сеттера, который возвращает void? 2. Почему это 'return_by_value'? –

+0

Указатель 'data' содержит адрес результата преобразования (целое число python). Документация Boost действительно неудовлетворительна по этой теме, поэтому мы должны смотреть исходные файлы. В [rvalue_from_python_data.hpp] (http://www.boost.org/doc/libs/1_61_0/boost/python/converter/rvalue_from_python_data.hpp) мы видим, что 'rvalue_from_python_storage' использует' boost :: python :: detail :: referent_storage' и 'boost :: add_reference', чтобы получить кусок памяти. – cromod

+0

Вы также можете взглянуть на [add_reference.hpp] (http://www.boost.org/doc/libs/1_61_0/boost/type_traits/add_reference.hpp) и [referent_storage.hpp] (http: // www .boost.org/док/ЛИЭС/1_61_0/импульс/питон/подробно/referent_storage.hpp). – cromod