Нужна помощь с ошибкой BOOST_FOREACH / компилятора - PullRequest
14 голосов
/ 21 мая 2010

Я знаю, что boost или compiler должен быть последним виноват, но я не вижу здесь другого объяснения. Я использую MSVC 2008 SP1 и повысить 1,43.

В следующем фрагменте кода выполнение никогда не покидает третий цикл BOOST_FOREACH

typedef Graph<unsigned, unsigned>::VertexIterator Iter;

Graph<unsigned, unsigned> g;
g.createVertex(0x66);

// works fine
Iter it = g.getVertices().first, end = g.getVertices().second;
for(; it != end; ++it)
    ;

// fine
std::pair<Iter, Iter> p = g.getVertices();
BOOST_FOREACH(unsigned handle, p)
    ;

// fine
unsigned vertex_count = 0;
BOOST_FOREACH(unsigned handle, g.getVertices())
    vertex_count++;

// oops, infinite loop
vertex_count = 0;
BOOST_FOREACH(unsigned handle, g.getVertices()) 
    vertex_count++;

vertex_count = 0;
BOOST_FOREACH(unsigned handle, g.getVertices())
    vertex_count++;

// ... last block repeated 6 times

Код итератора:

class Iterator 
    : public boost::iterator_facade<Iterator, unsigned const, 
                boost::bidirectional_traversal_tag>
{
public:
    Iterator()
        : list(NULL), handle(INVALID_ELEMENT_HANDLE)
    {}

    explicit Iterator(const VectorElementsList &list, unsigned handle = INVALID_ELEMENT_HANDLE)
        : list(&list), handle(handle)
    {}

    friend std::ostream& 
    operator<<(std::ostream &s, const Iterator &it)
    {
        s << "[list: " << it.list <<", handle: " << it.handle << "]";
        return s;
    }

private:
    friend class boost::iterator_core_access;

    void increment()
    {
        handle = list->getNext(handle);
    }

    void decrement() 
    {
        handle = list->getPrev(handle);
    }

    unsigned const& dereference() const
    {
        return handle; 
    }

    bool equal(Iterator const& other) const
    {
        return handle == other.handle && list == other.list;
    }

    const VectorElementsList<T> *list;
    unsigned handle;
};

Немного удовольствия от ASM:

    vertex_count = 0;
    BOOST_FOREACH(unsigned handle, g.getVertices())
// initialization
013E1369  mov         edi,dword ptr [___defaultmatherr+8 (13E5034h)] // end iterator handle: 0xFFFFFFFF
013E136F  mov         ebp,dword ptr [esp+0ACh] // begin iterator handle: 0x0
013E1376  lea         esi,[esp+0A8h] // begin iterator list pointer
013E137D  mov         ebx,esi 
013E137F  nop

// forever loop begin
013E1380  cmp         ebp,edi 
013E1382  jne         main+238h (13E1388h) 
013E1384  cmp         ebx,esi 
013E1386  je          main+244h (13E1394h) 
013E1388  lea         eax,[esp+18h] 
013E138C  push        eax
// here iterator is incremented in ram
013E138D  call        boost::iterator_facade<detail::VectorElementsList<Graph<unsigned int,unsigned int>::VertexWrapper>::Iterator,unsigned int const ,boost::bidirectional_traversal_tag,unsigned int const &,int>::operator++ (13E18E0h) 
013E1392  jmp         main+230h (13E1380h) 
        vertex_count++;
// forever loop end

Легко видеть, что дескриптор итератора кэшируется в EBP и никогда не увеличивается, несмотря на вызов функции итератора ++ ().
Я заменил имплементацию Itarator на тот, что был получен из std :: iterator, и проблема осталась, поэтому это не ошибка iterator_facade. Эта проблема существует только в выпусках msvc 2008 SP1 x86 и amd64. Отладка построена на msvc 2008, а отладка / выпуск построена на msvc 2010 и gcc 4.4 (linux) работает нормально. Кроме того, блок BOOST_FOREACH должен быть повторен ровно 10 раз. Если он повторяется 9 раз, все в порядке.

Полагаю, что из-за использования BOOST_FOREACH хитрости шаблона (const auto_any) компилятор предполагает, что дескриптор итератора является постоянным и никогда не считывает его реальное значение снова.

Я был бы очень рад услышать, что мой код неправильный, исправить его и двигаться дальше с помощью BOOST_FOREACH, который я очень нашел (в отличие от BOOST_FOREVER:).

Может быть связано с: Почему BOOST_FOREACH иногда не работает со строками C ++?

EDIT:

Я подготовил упрощенный проект, воспроизводящий проблему. Нет шаблонов, нет параметров по умолчанию, нет ничего. Получите это здесь: http://yabcok.nazwa.pl/ugly3.zip

Ответы [ 2 ]

2 голосов
/ 10 января 2013

Попробуйте добавить флаг компилятора / Oy (Свойства конфигурации -> C / C ++ -> Оптимизация -> Отключить «Пропустить указатель кадра»)

У меня была такая же проблема при использовании MSVC 2010, и это решило ее!

1 голос
/ 30 мая 2010

Похоже, я ошибка в VC ++ относительно значений по умолчанию в функциях шаблона.

Существует очень похожая ошибка здесь в августе 2009 года (закрыта как 'исправленная' M $ в их следующем выпуске) ... она согласована множеством способов: она специфична для VC ++ и работает в GCC приводит к периодическим сбоям с аргументами шаблона по умолчанию (но никогда не вызывает проблем во время компиляции), и проблема возникает только во втором экземпляре.

Тем не менее, я не могу объяснить вывод компилятора или магические 10 циклов ...: -)

VC ++ даже имеет старую статью о обходных путях с шаблонами . с очень похожими ошибками, появившимися в последнее время, и насколько последовательными кажутся ваша ошибка и ошибка Color_of_Green, вероятно, это VC ++, а не Boost.

Мое предположение? Он задыхается от этих подписей: const T & data = T() в graph_elements_collection.h. MSFT рекомендует изменить это значение на const T data = T(). Чтобы убедиться, что это связано с этой ошибкой компилятора, попробуйте это или обходные пути, опубликованные MSFT ... здесь ...

...