如何在绑定中使字符串类C++"own"自动转换为python字符串

How to make "own" C++ string class convert automatically to python strings in bindings

本文关键字:字符串 own 转换 python 绑定 C++      更新时间:2023-10-16

c++代码

class MyOwnString; // assume this implements a string class.
class HasMyOwnString // simple example that uses it
{
 private:
MyOwnString m_name;
int m_age;
  public:
HasMyOwnString( MyOwnString const& name, int age ) :
  m_name( name ), m_age( age )
{
}
MyOwnString name() const
{
    return m_name;
}
int age() const
{
    return m_age;
}
};
class HasStdString
{
   private:
std::string m_name;
int m_age;
   public:
HasStdString( std::string const& name, int age ) :
  m_name( name ), m_age( age )
{
}
std::string name() const
{
    return m_name;
}
int age() const
{
    return m_age;
}
};
BOOST_PYTHON_MODULE(test_cpp)
{
using namespace boost::python;
using boost::noncopyable;
class_<HasMyOwnString>( "HasMyOwnString", init<MyOwnString, int>() )
    .def( "name", &HasMyOwnString::name )
    .def( "age", &HasMyOwnString::age );
class_<HasStdString>( "HasStdString", init<std::string, int>() )
    .def( "name", &HasStdString::name )
    .def( "age", &HasStdString::age );
}

假设所有相关的标题都包含在内。上面的代码构建成python .pyd.

现在这是我想要工作的Python:

myStdStringTest = test_cpp.HasStdString( "UsesStdString", 1 )
name = myStdStringTest.name()
age = myStdStringTest.age()
print( name, age, type(name) )
myOwnStringTest = test_cpp.HasMyOwnString( "UsesOwnString", 2 )
name = myOwnStringTest.name()
age = myOwnStringTest.age()
print( name, age, type(name) )

Python的第一部分工作了,即HasStdString。它知道如何自动将Python str输入转换为std::字符串以使绑定生效。

第二部分期望MyOwnString失败。

    MyOwnString从const char *有一个隐式构造函数
  1. 我尝试了各种各样的事情,在一些boost的过载挂钩,但找不到任何工作。

我已经搜索了boost的代码,看看他们如何获得str自动转换为std::string。operator .hpp中的宏没有任何意义。在builtin_converters.hpp中的宏确实有意义,但它们似乎以另一种方式做所有的转换(除了它们在我的1.50.0版本中有编译器错误),并没有解决问题。

本质上,我试图暴露给Python的API有自己的字符串类。好吧,已经够糟糕的了,我无法改变。但我希望Python用户不会注意到这一点,并且我希望等效的Python API在c++代码中使用"自定义"字符串的地方使用Python字符串。我想在一个地方定义转换,然后让它在绑定函数的任何地方工作。

(为了那些谁想要一个MyOwnString实现工作,我做了我自己的一个在这里)

class MyOwnString
{
public:
    MyOwnString() 
      : m_internal()
    {
        init();
    }
    MyOwnString( const char * val ) 
        : m_internal()
    {
        init();
        assign( val, strlen( val ) );
    }
    MyOwnString( MyOwnString const& other )
        : m_internal()
    {
        init();
        assign( other.c_str(), other.m_len );
    }
    MyOwnString( const char * val, size_t len )
        : m_internal()
    {
        init();
        assign( val, len );
    }
    ~MyOwnString()
    {
        cleanup();
    }
    const char * c_str() const
    {
        return m_data;
    }
    size_t size() const
    {
        return m_len;
    }
    bool empty() const
    {
        return m_len == 0;
    }
    void clear()
    {
        cleanup();
        m_internal[0] = '';
    }
    MyOwnString & operator=( MyOwnString const& other )
    {
        assign( other.c_str(), other.m_len );
        return *this;
    }
    MyOwnString & operator=( const char * val )
    {
        assign( val, strlen(val) );
        return *this;
    }
private:
    void init()
    {
        m_data = m_internal;
        m_capacity = 0;
        m_len = 0;
    }
    void cleanup()
    {
        if( m_data != m_internal )
        {
            delete [] m_data;
            m_capacity = 0;
            m_data = m_internal;
        }
    }
    void assign( const char * text, size_t len ) // length not including null
    {
        if( len < EInternalSize ) // it fits into internal buffer
        {
            memcpy( m_internal, text, len );
            m_internal[len]='';
            cleanup(); // if we have dynamically allocated data remove it
            m_len = len;
        }
        else if( m_capacity > len ) // fits into already assigned buffer
        {
            memcpy( m_data, text, len );
            m_internal[len]='';
            m_len = len;
        }
        else
        {
            // We need to allocate. Do that before we delete any existing buffer
            char * buf = new char[ len + 1 ];
            memcpy( buf, text, len );
            buf[len] = '';
            cleanup(); // if there was a buffer there remove it
            m_data = buf;
            m_capacity = len + 1;
            m_len = len;
        }
    }

    enum { EInternalSize = 16 };
    size_t m_len;
    size_t m_capacity;
    char m_internal[EInternalSize];
    char * m_data;
};

总结这个问题是:

我需要在我的绑定中添加什么来允许HasMyOwnString的绑定与所提供的Python一样工作…

(我不介意在HasMyOwnString中改变init部分,我想要任何其他不需要我提供参数类型来工作的函数)。

在Boost.Python的FAQ中有答案。

您需要实现和注册合适的custom_string_to_python_strcustom_string_from_python_str函数。