У меня следующий (упрощенный) код:
class py_string {
PyObject* v;
public:
py_string(const std::string& str) {
// See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_FromStringAndSize
// The string data is copied into the returned object
v = PyUnicode_FromStringAndSize(str.data(), str.size());
}
~py_string() {
Py_XDECREF(v);
}
py_string(const py_string& other) {
v = other.v;
Py_XINCREF(v);
}
py_string(py_string&& other) {
v = other.v;
other.v = nullptr;
}
};
static py_string generate_stylesheet() {
std::ostringstream html; // html_styles.cc:80
html << "<style>";
html << ...; // html_styles.cc:82
html << "</style>";
return py_string(html.str()); // html_styles.cc:133
}
void emit() {
auto style = generate_stylesheet(); // html_styles.cc:138
...
}
При работе под Valgrind выдается следующая ошибка:
==5882== Invalid read of size 4
==5882== at 0x544028: ??? (in /usr/bin/python3.6)
==5882== by 0x6BBBFC2: emit() (html_styles.cc:138)
==5882== by 0x6D4AB0F: ...
==5882== Address 0x669b020 is 1,904 bytes inside a block of size 2,049 free'd
==5882== at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5882== by 0x6BBBB82: deallocate (new_allocator.h:125)
==5882== by 0x6BBBB82: deallocate (alloc_traits.h:462)
==5882== by 0x6BBBB82: _M_destroy (basic_string.h:226)
==5882== by 0x6BBBB82: _M_dispose (basic_string.h:221)
==5882== by 0x6BBBB82: ~basic_string (basic_string.h:647)
==5882== by 0x6BBBB82: ~basic_stringbuf (sstream:65)
==5882== by 0x6BBBB82: ~basic_ostringstream (sstream:590)
==5882== by 0x6BBBB82: generate_stylesheet() (html_styles.cc:80)
==5882== by 0x6BBBF4F: emit() (html_styles.cc:138)
==5882== by 0x6D4AB0F: ...
==5882== Block was alloc'd at
==5882== at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5882== by 0x715207C: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::reserve(unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
==5882== by 0x7145CA7: std::__cxx11::basic_stringbuf<char, std::char_traits<char>, std::allocator<char> >::overflow(int) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
==5882== by 0x71504AA: std::basic_streambuf<char, std::char_traits<char> >::xsputn(char const*, long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
==5882== by 0x7140B83: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25)
==5882== by 0x6BBB86A: operator<< <std::char_traits<char> > (ostream:561)
==5882== by 0x6BBB86A: generate_stylesheet() (html_styles.cc:82)
==5882== by 0x6BBBF4F: emit() (html_styles.cc:138)
Итак, похоже, что Valgrind сообщает мне следующая история: в строке 138 (функция emit()
) есть попытка прочитать данные из строки, которая больше не существует, но ранее принадлежала объекту html
внутри функции generate_stylesheet()
, а затем была освобождена при выходе из функции.
Я всегда понимал, что ostringstream::str()
создает и возвращает копию строки, содержащей данные потока. Но этот объект, даже если он временный, должен дожить до конца полного выражения, и до того, как это произойдет, строка будет скопирована в PyObject*
.
Итак, что здесь происходит?