在Boost Python中使用Unicode的C++函数,参数为std::wstring

3 投票
1 回答
2572 浏览
提问于 2025-04-18 16:36

我正在使用Boost Python库来包装一个C++类,这样我就可以从Python中调用它的方法。我的C++类叫做Clazz,它有两个公开的方法:

void doSomething(std::string& s) { ... }
void doSomethingWide(std::wstring& ws) { ... }

我创建了一个BOOST_PYTHON_MODULE,指向这两个方法。第一个方法使用std::string,我可以正常调用。但是,当我尝试用Python的Unicode字符串调用第二个方法时:

x = u'hello'
Clazz.doSomethingWide(x)

我遇到了这个错误:

ArgumentError: Python argument types in Clazz.doSomethingWide(Clazz, unicode) did not match C++ signature: doSomething(Clazz, std::wstring)

我原本以为unicode会像普通的Python字符串那样自动与std::wstring对接,但事实并非如此。

在另一个讨论中,有人建议先进行转换:

x = str(x.encode('utf-8'))

不过,我处理的是非常大的字符串,这样做会严重影响我的代码性能,因为这个转换的复杂度是O(n),也就是与x的字符数量成正比。

确实可以修改我想要对接的C++库。有没有办法将Python的unicode类型传递到我的C++库中,以便我可以使用它们?我在网上搜索了很久,找到了一些关于转换器和其他东西的参考,但实现这些并没有解决上面的错误信息(很可能是我没有正确使用它们)。

1 个回答

2

简单来说,类型转换通常会产生一个临时对象,所以参数必须通过值传递或者常量引用来接受。因此,你需要把:

void doSomethingWide(std::wstring&);

改成以下任意一种:

void doSomethingWide(std::wstring);
void doSomethingWide(const std::wstring&);

Boost.Python在2003年9月11日增加了对 std::wstring 的转换。一般来说,当在Boost.Python中发生类型转换时,生成的对象会被视为一个临时对象。这种行为在 boost::python::extract 的观察者规范中间接提到:

将存储的指针转换为 result_type,这个类型要么是 T,要么是 T const&

如果支持左值转换,可能会给某些类型带来奇怪的语义。例如,不可变的Python字符串可能会被C++函数修改。


下面是一个完整的最小示例:

#include <iostream>
#include <string>
#include <boost/python.hpp>

class spam
{
public:
  void doSomething(const std::string& str)
  {
    std::cout << "spam::doSomething(): " << str << std::endl;
  }

  void doSomethingWide(const std::wstring& str)
  {
    std::wcout << "spam::doSomethingWide(): " << str << std::endl;
  }
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<spam>("Spam")
    .def("doSomething", &spam::doSomething)
    .def("doSomethingWide", &spam::doSomethingWide)
    ;
}

交互式使用:

>>> import example
>>> spam = example.Spam()
>>> spam.doSomething("test")
spam::doSomething(): test
>>> spam.doSomethingWide(u"test")
spam::doSomethingWide(): test

撰写回答