Is there a way to wrap boost::optional<T>
type object to expose it via boost::python::class_
(used from BOOST_PYTHON_MODULE
)
struct Foo
{
boost::optional<int> bar;
};
BOOST_PYTHON_MODULE(module_name)
{
class_<Foo>("Foo")
.def_readwrite("bar", &Foo::bar);
}
What I expect in Python is AttributeError in this case
import module_name
f = module_name.Foo()
print f.bar
as the value of bar
hasn't been set yet.
And TypeError
when
import module_name
f = module_name.Foo()
f.bar = "string"
bar
is of int
type.
The other related problem is to export, in the same fashion, classes' objects of boost::python::indexing_suite
container types.
Is the problem solvable using boost::python
api?
You need an exception translator and python converters.
Exception translator
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-python converter
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> >();
...
}
From-python converter
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>();
...
}
Moreover you can't use converters with def_readwrite
(see this FAQ), you have to use 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>()));
}
Thus you'll get those outputs in your Python interpreter :
>>> 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With