拥有字符串的用法
我有自己的字符串类,想把它导出到Python中(使用boost.python),并作为原生字符串使用。我为此写了一些转换器。
第一个是导出我的字符串:
bp::class_<CL_StringRef8>("CL_StringRef8", bp::init<const std::string&>())
.def("CStr", &CL_StringRef8::c_str);
这些转换器是从默认的教程中来的,不过是针对我的类型:
// CL_StringRef8 → Python string --------------------------------------
struct cl_stringref8_to_python_str
{
static PyObject* convert(CL_StringRef8 const& s)
{
return boost::python::incref(boost::python::object(s.c_str()).ptr());
}
};
// Python string → CL_StringRef8 --------------------------------------
struct cl_stringref8_from_python_str
{
cl_stringref8_from_python_str()
{
bp::converter::registry::push_back(
&convertible,
&construct,
boost::python::type_id<CL_StringRef8>()
);
}
static void* convertible(PyObject* obj_ptr)
{
if (!PyString_Check(obj_ptr)) return 0;
return obj_ptr;
}
static void construct(PyObject* obj_ptr, bp::converter::rvalue_from_python_stage1_data* data)
{
const char* value = PyString_AsString(obj_ptr);
if (value == 0) bp::throw_error_already_set();
void* storage = ((bp::converter::rvalue_from_python_storage<CL_StringRef8>*)data)->storage.bytes;
new (storage) CL_StringRef8(value);
data->convertible = storage;
}
};
现在,按照我的理解,我可以从Python中调用C++函数,这些函数接受CL_StringRef8
作为参数,但我有一段代码:
print GetMyStringObject()
data = float(GetMyStringObject())
# ==>
<CL_StringRef object at 0x7fd15dfd9de8>
TypeError: float() argument must be a string or a number
我理解我需要为CL_StringRef8
导出str()
方法,但我做不到:
bp::class_<CL_StringRef8>("CL_StringRef8", bp::init<const std::string&>())
.def("CStr", &CL_StringRef8::c_str)
.def(bp::self_ns::str(bp::self_ns::self));
最后一行报错了:
/usr/include/boost/lexical_cast.hpp: In member function ‘bool boost::detail::lexical_stream<Target, Source, Traits>::operator<<(const Source&) [with Target = std::basic_string<char>, Source = CL_StringRef8, Traits = std::char_traits<char>]’:
/usr/include/boost/lexical_cast.hpp:1151:13: instantiated from ‘Target boost::detail::lexical_cast(typename boost::call_traits<Source>::param_type, CharT*, std::size_t) [with Target = std::basic_string<char>, Source = CL_StringRef8, bool Unlimited = true, CharT = char, typename boost::call_traits<Source>::param_type = const CL_StringRef8&, std::size_t = long unsigned int]’
/usr/include/boost/lexical_cast.hpp:1174:77: instantiated from ‘Target boost::lexical_cast(const Source&) [with Target = std::basic_string<char>, Source = CL_StringRef8]’
/usr/include/boost/python/operators.hpp:357:1: instantiated from ‘static PyObject* boost::python::detail::operator_1<(boost::python::detail::operator_id)19u>::apply<T>::execute(boost::python::detail::operator_1<(boost::python::detail::operator_id)19u>::apply<T>::self_t&) [with T = CL_StringRef8, PyObject = _object, boost::python::detail::operator_1<(boost::python::detail::operator_id)19u>::apply<T>::self_t = CL_StringRef8]’
/usr/include/boost/python/operators.hpp:152:11: instantiated from ‘void boost::python::detail::operator_<id, L, R>::visit(ClassT&) const [with ClassT = boost::python::class_<CL_StringRef8>, boost::python::detail::operator_id id = (boost::python::detail::operator_id)19u, L = boost::python::detail::not_specified, R = boost::python::detail::not_specified]’
/usr/include/boost/python/def_visitor.hpp:31:9: instantiated from ‘static void boost::python::def_visitor_access::visit(const V&, classT&) [with V = boost::python::def_visitor<boost::python::detail::operator_<(boost::python::detail::operator_id)19u> >, classT = boost::python::class_<CL_StringRef8>]’
/usr/include/boost/python/def_visitor.hpp:67:9: instantiated from ‘void boost::python::def_visitor<DerivedVisitor>::visit(classT&) const [with classT = boost::python::class_<CL_StringRef8>, DerivedVisitor = boost::python::detail::operator_<(boost::python::detail::operator_id)19u>]’
/usr/include/boost/python/class.hpp:225:9: instantiated from ‘boost::python::class_<T, X1, X2, X3>::self& boost::python::class_<T, X1, X2, X3>::def(const boost::python::def_visitor<Derived>&) [with Derived = boost::python::detail::operator_<(boost::python::detail::operator_id)19u>, W = CL_StringRef8, X1 = boost::python::detail::not_specified, X2 = boost::python::detail::not_specified, X3 = boost::python::detail::not_specified, boost::python::class_<T, X1, X2, X3>::self = boost::python::class_<CL_StringRef8>]’
/home/ockonal/Workspace/Themisto/src/Scripts/Core/TypesConverters.cpp:375:49: instantiated from here
/usr/include/boost/lexical_cast.hpp:595:48: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.0/../../../../include/c++/4.6.0/ostream:581:5: error: initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = CL_StringRef8]’
ps 对了,我没有忘记把我的转换器注册到Python中。
1 个回答
2
为了让 .def(bp::self_ns::str(bp::self_ns::self));
这一行代码正常工作,你需要为你的类定义一个流操作符。
std::ostream & operator<<(std::ostream & os, const CL_StringRef8 &s)
{ os << s.c_str(); return os; }
还有一种不需要流操作符就能定义 str() 方法的方法是:
.def("__str__", &CL_StringRef8::c_str)
不过,我更喜欢直接使用 repr() 方法,这样在需要的时候 str() 就会用到它。
.def("__repr__", &CL_StringRef8::c_str)
不过,这些做法仍然无法让 float() 在 Python 中正常工作,因为它需要的是一个字符串(而不是对象,即使这个对象支持 __str__),或者一个浮点数,或者一个支持 __float__ 的对象。
不过,我们可以通过在你的 c_str() 方法周围绑定一个 lexical_cast 来实现后者:
#include <boost/lexical_cast.hpp>
#include <boost/mpl/vector.hpp>
然后
.def("__float__", bp::make_function(
boost::bind(&boost::lexical_cast<float,CL_StringRef8>,_1),
bp::default_call_policies(),
boost::mpl::vector<float,CL_StringRef8>()));
现在你可以在 Python 中这样做:
>>> a = CL_StringRef8("1.234")
>>> print a
1.234
>>> float(a) * 2
2.468