2016-07-12 4 views
1

Я провел некоторое время вчера, отлаживая прекрасный Гейзенбюг, где меня изменил тип xmlNodePtr. Этот пример показывает ошибку:libxml2 Тип xmlNodePtr меняется, когда я его наблюдаю

#include <iostream> 

#include <vector> 
#include <string> 
#include <memory> // std::unique_ptr 

#include <cstdint> 

#include <libxml/tree.h> 
#include <libxml/parser.h> 

struct SomeDataType { 
    std::vector<std::vector<std::string>> data; 

    explicit SomeDataType(uint32_t rows_, uint32_t columns_) 
     : data(rows_) 
    { 
     for (uint32_t row = 0; row < rows_; ++row) { 
      data[row].resize(columns_); 
     } 
    } 
}; 

static std::vector<xmlNodePtr> GetChildren(xmlNodePtr node) 
{ 
    std::vector<xmlNodePtr> children; 

    xmlNodePtr child = node->children; 
    while (child) { 
     if (child->type == XML_ELEMENT_NODE) { 
      children.push_back(child); 
     } 
     child = child->next; 
    } 

    return children; 
} 

int main() { 
    std::unique_ptr<xmlDoc, void(*)(xmlDoc*)> document = { xmlParseEntity("libxml2-fail.xml"), xmlFreeDoc }; 

    SomeDataType{ 3, 2 }; 

    xmlNodePtr root = xmlDocGetRootElement(document.get()); 

    for (const xmlNodePtr &child : GetChildren(root)) { 
     const xmlNodePtr &entry = GetChildren(child)[0]; // Problem here... 
     std::cout << "Expected " << XML_ELEMENT_NODE << " but was " << entry->type << std::endl; 
     std::cout << entry->name << std::endl; 
    } 
} 

Составлено с:

g++ -g -std=c++14 -Wall -Wextra -pedantic -I/usr/include/libxml2 libxml2-fail.cpp -lxml2 -o fail.out 

Файл XML:

<?xml version="1.0" encoding="utf-8"?> 
<data> 
    <tag> 
    <subtag>1</subtag> 
    </tag> 
</data> 

Бег дает мне следующий вывод:

Expected 1 but was 17 

пошагового с gdb, все, что я до тех пор, пока мы не достигнем линии const xmlNodePtr & = .... Вместо типа XML_ELEMENT_NODE он имеет тип XML_ENTITY_DECL. Однако, если я выполнить следующие команды, ссылка xmlNodePtr морфы типа я ожидал:

48   const xmlNodePtr &entry = GetChildren(child)[0]; 
(gdb) n 
49   std::cout << "Expected " << XML_ELEMENT_NODE << " but was " << entry->type << std::endl; 
(gdb) p *entry 
$1 = {_private = 0x0, type = XML_ENTITY_DECL, name = 0x0, children = 0xb7e67d7c <std::string::_Rep::_S_empty_rep_storage+12>, last = 0x0, parent = 0x69, next = 0x0, prev = 0x9, doc = 0x0, ns = 0x805edb8, content = 0x805edb8 "", properties = 0x0, nsDef = 0x0, psvi = 0x0, line = 60648, extra = 2053} 
(gdb) p *child 
$2 = {_private = 0x0, type = XML_ELEMENT_NODE, name = 0x805ee98 "tag", children = 0x805eea8, last = 0x805ef98, parent = 0x805edb8, next = 0x805efe8, prev = 0x805ee08, doc = 0x805ece8, ns = 0x0, content = 0x0, properties = 0x0, nsDef = 0x0, psvi = 0x0, line = 3, extra = 0} 
(gdb) p GetChildren(child) 
$3 = std::vector of length 1, capacity 1 = {0x805eef8} 
(gdb) p *entry 
$4 = {_private = 0x0, type = XML_ELEMENT_NODE, name = 0x805ef38 "subtag", children = 0x805ef48, last = 0x805ef48, parent = 0x805ee58, next = 0x805ef98, prev = 0x805eea8, doc = 0x805ece8, ns = 0x0, content = 0x0, properties = 0x0, nsDef = 0x0, psvi = 0x0, line = 4, extra = 0} 
(gdb) 

У меня нет проблемы, когда я вместо того, чтобы цикл по одному элементу, как так:

for (const xmlNodePtr &entry : GetChildren(child)) { 
    ... 
} 

я тоже не проблема, когда я не сделать xmlNodePtr константной ссылки, как так:

xmlNodePtr entry = GetChildren(child)[0]; 

Однако, по словам this stackoverflow question, это не должно быть проблемой.

Необходимо, чтобы структура SomeDataType была необходима; в противном случае я получаю segfault, потому что entry становится нулевым указателем.

Что это за ошибка?

ответ

3

Когда вы сделаете это:

const xmlNodePtr &entry = GetChildren(child)[0]; // Problem here... 

Вы эффективно связывать ссылку на временный таким образом, что это не срок службы продлен. operator[] возвращает ссылку, поэтому вы не привязываете ссылку на временное - вы привязываете ссылку на ссылку. Но эта возвращаемая ссылка от operator[] относится к элементу в базовом временномvector, который возвращается GetChildren(), который выходит за пределы области действия в конце строки, оставляя себя оборванной ссылкой.


Однако, когда вы вместо этого пытались:

for (const xmlNodePtr &entry : GetChildren(child)) { 

что синтаксический сахар для:

{ 
    auto&& __range = GetChildren(child); // bind temporary to reference 
             // lifetime IS extended 
    auto b = begin(__range); 
    auto e = end(__range); 
    for (; b != e; ++b) { 
     const xmlNodePtr& entry = *b; 
     // ... 
    } 
} 

здесь, *b не является временным или какая-либо часть временного - это ссылку в контейнер, срок службы которого длится до тех пор, пока __range, который проходит через весь корпус цикла. Нет болтающихся ссылок.


Аналогично,

xmlNodePtr entry = GetChildren(child)[0]; 

не просто копирование, никаких ссылок проблем вообще.

+0

Хорошо, это имеет смысл. Я предполагаю, что gdb хранит возвращаемое значение 'GetChildren', которое только что делает ссылку действительной снова (UB хотя) – Justin

Смежные вопросы