Is this a Visual Studio 2013 update 4 C++ optimizer bug or is my code wrong?
Asked Answered
O

1

8

I have been seeing crashes in our software since I updated to boost 1.58 and VS2013. Only when compiler optimization is on we see that crashes. With boost 1.55 there are no crashes. I've managed to isolate the problem I'm seeing to boost::any_range and how we use it.

See the example code below:

#include <boost/range/any_range.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <vector>
#include <memory>
#include <cstdio>

class DummyElement
{
public:
    float f_;
};

using ElementRange = boost::any_range < DummyElement*, boost::bidirectional_traversal_tag >;

using DummyElementUPtr = std::unique_ptr < DummyElement > ;

class BoostAnyTest
{
public:
    BoostAnyTest()
    { 
        for (int i = 0; i < 10; ++i)
        {
            auto element = DummyElementUPtr(new DummyElement());
            _tprintf(_T("BoostAnyTest::ctor() 0x%p\n"), element.get());

            c_.emplace_back(std::tuple<Int, DummyElementUPtr>(i, std::move(element)));
        }
    }

public:
    ElementRange GetAll();

private:
    using _ContainerType = std::vector < std::tuple<Int, std::unique_ptr<DummyElement>> > ;
    _ContainerType    c_;
};


ElementRange
BoostAnyTest::GetAll()
{
    auto transform = [ ] (const _ContainerType::value_type& v) -> DummyElement*
    {
        return std::get<1>(v).get();
    };

    return c_ | boost::adaptors::transformed(transform);
}


int
main()
{
    BoostAnyTest    any;

    auto range = any.GetAll();

    std::for_each(std::begin(range), std::end(range), [ ] (DummyElement* element)
    { 
        _tprintf(_T("TestBoostAnyRange() 0x%p\n"), element);
    });
}

Below is the program output. The DEBUG version output is what I expect, but the optimized RELEASE version is a mistery to me at the moment...

DEBUG version output:
BoostAnyTest::ctor() 0x007D0FB0
BoostAnyTest::ctor() 0x007D0E30
BoostAnyTest::ctor() 0x007D0E60
BoostAnyTest::ctor() 0x007D1160
BoostAnyTest::ctor() 0x007D0E90
BoostAnyTest::ctor() 0x007D10A0
BoostAnyTest::ctor() 0x007D0F80
BoostAnyTest::ctor() 0x007D0FE0
BoostAnyTest::ctor() 0x007D1010
BoostAnyTest::ctor() 0x007D1040
TestBoostAnyRange() 0x007D0FB0
TestBoostAnyRange() 0x007D0E30
TestBoostAnyRange() 0x007D0E60
TestBoostAnyRange() 0x007D1160
TestBoostAnyRange() 0x007D0E90
TestBoostAnyRange() 0x007D10A0
TestBoostAnyRange() 0x007D0F80
TestBoostAnyRange() 0x007D0FE0
TestBoostAnyRange() 0x007D1010
TestBoostAnyRange() 0x007D1040

RELEASE version output:
BoostAnyTest::ctor() 0x00BFA358
BoostAnyTest::ctor() 0x00BFA238
BoostAnyTest::ctor() 0x00BFA3E8
BoostAnyTest::ctor() 0x00BFA248
BoostAnyTest::ctor() 0x00BFA258
BoostAnyTest::ctor() 0x00BFA268
BoostAnyTest::ctor() 0x00C2ECB8
BoostAnyTest::ctor() 0x00C2ED98
BoostAnyTest::ctor() 0x00C2EDA8
BoostAnyTest::ctor() 0x00C2ED48
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0
TestBoostAnyRange() 0x00A5FCE0

Maybe my code is wrong or is it really a bug in the optimizer? Any tips would be greatly appreciated!

Officinal answered 14/7, 2015 at 9:30 Comment(6)
_ContainerType is a reserved name (because it begins with an underscore followed by a capital letter). Not related to your problem, though.Elegance
Doesn't look like a compiler bug: coliru.stacked-crooked.com/a/edcdb838034085caElegance
Please present a compilable minimal testcase. It would have a main function. Also... turn on your compiler warnings!!!!!!!!!!!!!!Crusade
Looks a bit like a use-after-free; the following works as expected: coliru.stacked-crooked.com/a/d6bd88aac65c0d15 But the lifetime semantics of the boost types involved are not clear to me.Elegance
Boost.Range does not play nice with temporary rvalue ranges, because it is only a view onto a range and does not own its elements, so it's very easy to get dangling pointers. I suspect you are getting a dangling reference into the result of the transform filter.Movable
In addition to the splendid find by ecatmur, what @JonathanWakely is true. Don't store your range adaptations in a named object because it will have dangling references to temporaries inside. In your case I'd use Boost Multi-index, reallyKowalewski
N
11

This is Boost bug 10493 (introduced in Boost 1.56, which is why your code works with Boost 1.55).

The workaround is to use T const as the Reference template parameter, in your case:

using ElementRange = boost::any_range <
    DummyElement*,
    boost::bidirectional_traversal_tag,
    DummyElement* const
>;

Example.

Nitriding answered 14/7, 2015 at 11:13 Comment(1)
Thanks a lot to all for looking into this and for your answers and suggestions!Officinal

© 2022 - 2024 — McMap. All rights reserved.