[C++-sig] function types as user-defined converters
troy d. straszheim
troy at resophonic.com
Fri Nov 6 20:52:49 CET 2009
>> I'm wondering if there isn't motivation here to cleanly integrate a
>> general facility for additional c++-side type conversions.
I got distracted with boost.cmake stuff for a while but just got some
things working. Simple example:
// type hidden from python
struct Hidden
{
double value;
};
//
// c++ interface that uses hidden type
//
std::string takes_hidden(Hidden h)
{
return std::string("Got an Hidden containing ")
+ boost::lexical_cast<std::string>(h.value);
}
//
// converter object
//
struct dbl_to_Hidden
{
typedef Hidden result_type;
result_type operator()(double v) const
{
Hidden h;
h.value = v;
return a;
}
};
// the module
BOOST_PYTHON_MODULE(udconversions_ext)
{
def("takes_hidden",
as<std::string(dbl_to_Hidden(double))>(&takes_hidden));
} ^^^^^^^^^^^^^^^^^^^^^
And the python:
>>> from udconversions_ext import *
>>> takes_a(13)
'Got an A containing 13'
Here's another contrived example:
// Here we have a class that takes optional<int> arguments that
// python ought not know about:
struct S
{
void set(optional<int> v)
{
i = v;
}
void add(boost::optional<int> oi)
{
if (i && oi)
(*i) += (*oi);
}
std::string show()
{
if (i)
return boost::lexical_cast<std::string>(*i);
else
return "<unset>";
}
optional<int> i;
};
// And a converter function object that converts a python::object
// to an optional<T> (holding a T) if the object is
// convertible to T, otherwise it returns an empty optional<T>:
template <typename T>
struct to_optional
{
typedef optional<T> result_type;
result_type operator()(const bp::object& o)
{
bp::extract<T> ex(o);
if (ex.check())
return optional<T>(ex());
else
return optional<T>();
}
};
BOOST_PYTHON_MODULE(opt_ext)
{
bp::class_<S>("S")
.def("set", bp::as<void(S*, to_optional<int>(bp::object))>(&S::set))
.def("add", bp::as<void(S*, to_optional<int>(bp::object))>(&S::add))
.def("show", &S::show) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;
};
And the result:
>>> from opt_ext import *
>>> s = S()
>>> s.set(13)
>>> s.show()
'13'
>>> s.set(None)
>>> s.show()
'<unset>'
>>> s.set(None)
>>> s.add(13)
>>> s.show()
'<unset>'
>>> s.set(13)
>>> s.add(13)
>>> s.show()
'26'
There are plenty of issues yet.
Currently, the converter type still leaks out to python in the signature:
class S(Boost.Python.instance)
| add(...)
| add( (S)arg1, (object)arg2) -> None :
|
| C++ signature :
| void add(S*,to_optional<int>
(*)(boost::python::api::object))
converters won't nest:
def("foo", as<returnvalue(arg1, conv1(conv2(arg2)))>(&somefn)); // nah
I dont have a mechanism for hooking a user defined converter into the
*return* type. I'm going to have to think about that one a bit more.
specifying a function type, whose return value is a pointer to a
function, is kinda nasty anyway:
def("foo", as<(converter(*)(int))(bool, converter(float))>(&fn));
I'm just not sure it gets one anything other than terseness. Maybe
a special return value policy is in order.
Also, there isn't a way to use an object's constructor as a converter,
That is, I'd like to support this syntax:
struct X {
X(double);
};
void takes_X(X x);
def("takes_x", as<void(X(double))>(&takes_x));
But I think this involves adding some scaffolding so that
boost.python can differentiate between what in proto-ese is a Callable
Transform (what now works) and an Object Transform (convert
via constructor).
-t
More information about the Cplusplus-sig
mailing list