UnicodeDecodeError с pybind11 - PullRequest
       8

UnicodeDecodeError с pybind11

0 голосов
/ 23 апреля 2019

Я пытаюсь обернуть класс, который возвращает строку.

class SS {
  public:
    SS(const std::string& s) : data_(s.data()), size_(s.size()) {}

    // Return a pointer to the beginning of the referenced data
    const char* data() const { return data_; }

    const char* data_;
    size_t size_;
};

class PySS: public SS {
  public:
    PySS(const std::string &str): SS(str) {
      std::cout << "cons " << str << std::endl; #key1
      std::cout << "data " << SS::data() << std::endl; # key1

    }

    std::string data() {
      std::cout << "call data " << SS::data() << std::endl; # p��
      return std::string(SS::data());
    }
};

void init_slice(py::module & m) {
  py::class_<PySS>(m, "SS")
    .def(py::init<const std::string&>())
    .def("data", &PySS::data);
}

При звонке с python,

s = SS('key1')
print (s.data())

выдает ошибку Unicode

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xae in position 1: invalid start byte

Я печатаю строку в конструкторе, и она показывает идентичный результат. Но в другой функции он показывает неинтерпретированную строку.

есть идеи?

[Изменить]

Вот минимальный пример воспроизведения подобной проблемы.

class SS {
  public:
    SS(const std::string& s) : data_(s.data()) {}

    // Return a pointer to the beginning of the referenced data
    const char* data() const { return data_; }
    const std::string ToString() const {
      std::cout << std::string(data_) << std::endl;
      return std::string(data_);
    }

    const char* data_;
};

void init_slice(py::module & m) {
  py::class_<SS>(m, "Slice")
  .def(py::init<const std::string&>())
  .def("data", &SS::ToString);
}

1 Ответ

0 голосов
/ 23 апреля 2019

Проблема и решение

В вашем примере есть несколько проблем, самая важная из которых состоит в том, что ваши указатели недействительны, поскольку они указывают на то, что выходит за рамки (ваш аргумент sclass SS).

Решение состоит в том, чтобы скопировать s в переменную-член class SS следующим образом:

#include <string>
#include <iostream>
#include <pybind11/pybind11.h>

namespace py = pybind11;

class SS {
  public:
    SS(const std::string& s) : m_data(s) {}

    const char* data() const { return m_data.data(); }

    std::string m_data;
};

class PySS: public SS {
  public:
    PySS(const std::string& s): SS(s) {}

    std::string get() { return std::string(SS::data()); }
};

PYBIND11_MODULE(example, m)
{
  py::class_<PySS>(m, "SS")
    .def(py::init<const std::string&>())
    .def("get", &PySS::get);
}

Еще два замечания:

  • В вашем примере отсутствует макрос PYBIND11_MODULE, который заботится о некоторых общих вещах, чтобы можно было импортировать ваш модуль (см. этот пример ).
  • Я бы никогда не объявилта же функция с двумя различными значениями: ваш SS::data() возвращает указатель, а PySS::data() возвращает копию (std::string).Таким образом, я переименовал последнее в PySS::get(), чтобы сделать различие ясным.

Обходной путь для сторонних классов

Учитывая, что вы class SS это вне вашего контроля, я думаю, что вы можете обойти проблему, только обернув ее.Например:

#include <string>
#include <iostream>
#include <pybind11/pybind11.h>

namespace py = pybind11;

class SS {
  public:
    SS() = default;
    SS(const std::string& s) : data_(s.data()), size_(s.size()) {}
    const char* data() const { return data_; }

  private:
    const char* data_;
    size_t size_;
};

class PySS {
  public:
    PySS(const std::string& s) { m_data = s; m_SS = SS(m_data); }
    std::string get() { return std::string(m_SS.data()); }

  private:
    std::string m_data;
    SS m_SS;
};

PYBIND11_MODULE(example, m)
{
  py::class_<PySS>(m, "SS")
    .def(py::init<const std::string&>())
    .def("get", &PySS::get);
}
...