【问题标题】:Need help with BOOST_FOREACH/compiler bug需要有关 BOOST_FOREACH/编译器错误的帮助
【发布时间】:2011-02-22 07:33:32
【问题描述】:

我知道应该最后归咎于 boost 或编译器,但我在这里看不到其他解释。 我正在使用 msvc 2008 SP1 和 boost 1.43。

在下面的代码中,sn-p 执行永远不会离开 第三个 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 中,尽管调用了迭代器 operator++() 函数,但它永远不会递增。
我已将 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 :)。

可能与:Why does BOOST_FOREACH not work sometimes with C++ strings?

编辑:

我已经准备了重现问题的简化项目。没有模板,没有默认参数,什么都没有。在这里获取:http://yabcok.nazwa.pl/ugly3.zip

【问题讨论】:

  • 能否提供可编译的测试用例?另外,您是否尝试过将两个循环的汇编代码与相同的 C++ 代码进行比较?循环必须重复 10 次这一事实暗示这可能是与优化相关的编译器错误。
  • 013E1392 怎么回事?
  • 跳转到 013E1380 - 永远循环的开始
  • 这篇文章已经有将近 3 年的历史了......您是否收到过 Microsoft 或 Boost 的任何反馈?

标签: c++ visual-c++ boost boost-foreach


【解决方案1】:

尝试添加 /Oy- 编译器标志(配置属性 -> C/C++ -> 优化 -> 禁用“省略帧指针”)

我在使用 MSVC 2010 时遇到了同样的问题,这解决了它!

【讨论】:

    【解决方案2】:

    在我看来,bug in VC++ 关于模板函数中的默认值。

    在 2009 年 8 月有一个非常相似的错误 here(在其下一个版本中被 M$ '修复'关闭)...它在很多方面都是一致的:它是 VC++ 特定的并且在 GCC 中工作,导致间歇性故障使用默认模板参数(但绝不会出现编译时问题),并且问题只会在第二次实例化时出现。

    也就是说,我无法解释编译器的输出,或者神奇的 10 个循环... :-)

    VC++ 甚至有一篇关于workarounds with templates 的旧文章。最近有非常相似的错误,而且你的错误和 Color_of_Green 的错误看起来有多一致,它可能是 VC++ 而不是 Boost。

    我的猜测?这些签名令人窒息:graph_elements_collection.h 中的const T &amp; data = T()。 MSFT 建议将其更改为 const T data = T()。要验证它是否与此编译器错误有关,请尝试这样做,或者 MSFT 发布的解决方法...here...

    【讨论】:

    • 由于 Visual C++ 2010 现已推出,您是否尝试过使用较新的编译器重复此操作以查看问题是否消失?如果是这样,您可以确定这是一个已修复的错误。
    • 不知道为什么你会做const t data = T() 而不仅仅是const t
    • @DeadMG:对不起,我不明白,你能详细说明一下吗?
    • 你已经默认构造了一个T,所以..你可以用它复制构造一个T吗?为什么不直接构建一个 T?
    • @Kate 不完全是因为编译器中的其他更改可能会影响重现问题或其他一些细节所需的循环数。这个错误对堆栈布局和空间辐射非常敏感 @DeadMG 使用 fn(const t data = T()) 您可以在没有参数的情况下调用 fn @robert 感谢您的建议,但似乎这不是我的错误。我在这里制作了我的测试用例的更简单版本(根本没有默认参数):geneviz.googlecode.com/files/ugly2.zip
    猜你喜欢
    • 1970-01-01
    • 2011-02-21
    • 2023-03-08
    • 2020-06-17
    • 1970-01-01
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 2015-04-13
    相关资源
    最近更新 更多