Googletest: Infinite Loop when calling a mock function that takes boost::filesystem::path as parameter

Created on 26 Aug 2015  Â·  13Comments  Â·  Source: google/googletest

_From @GoogleCodeExporter on August 24, 2015 22:40_

Minimal example:

---------------------
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <boost/filesystem.hpp>

class MyMock {
public:
  MOCK_METHOD1(func, void(const boost::filesystem::path &path));
};

TEST(MyTest, mockTest) {
  MyMock mock;
  mock.func(boost::filesystem::path("/myfile"));
}
-------------------

When running this test (using gtest 1.7.0 and gmock 1.7.0 on linux 64bit with 
g++ (Ubuntu 4.9.1-16ubuntu6)), it aborts with a SIGSEV.
gdb shows that the SIGSEV is raised in malloc(), while the stack is very large 
and contains a short periodic set of functions calling each other in a circle. 
I assume there is an infinite loop.

The interesting part of the stacktrace is here:


#0  _int_malloc (av=0x7ffff703a760 <main_arena>, bytes=26) at malloc.c:3302
#1  0x00007ffff6cff1e0 in __GI___libc_malloc (bytes=26) at malloc.c:2891
#2  0x00007ffff72b7688 in operator new(unsigned long) () from 
/usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff731b669 in std::string::_Rep::_S_create(unsigned long, unsigned 
long, std::allocator<char> const&) () from 
/usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff731b6f4 in std::string::_M_mutate(unsigned long, unsigned long, 
unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff731bd2e in std::string::_M_replace_safe(unsigned long, unsigned 
long, char const*, unsigned long) () from 
/usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007ffff731d1f9 in std::string::replace(unsigned long, unsigned long, 
char const*, unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7  0x00007ffff7779821 in boost::filesystem::path::begin() const () from 
/usr/lib/x86_64-linux-gnu/libboost_filesystem.so.1.55.0
#8  0x00000000004c3cd4 in 
testing::internal::DefaultPrintTo<boost::filesystem::path> (container=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:275
#9  0x00000000004c3846 in testing::internal::PrintTo<boost::filesystem::path> 
(value=..., os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:376
#10 0x00000000004c2ea6 in 
testing::internal::UniversalPrinter<boost::filesystem::path>::Print (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:600
#11 0x00000000004c237d in 
testing::internal::UniversalPrint<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:756
#12 0x00000000004c3d37 in 
testing::internal::DefaultPrintTo<boost::filesystem::path> (container=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:287
#13 0x00000000004c3846 in testing::internal::PrintTo<boost::filesystem::path> 
(value=..., os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:376
#14 0x00000000004c2ea6 in 
testing::internal::UniversalPrinter<boost::filesystem::path>::Print (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:600
#15 0x00000000004c237d in 
testing::internal::UniversalPrint<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:756
#16 0x00000000004c3d37 in 
testing::internal::DefaultPrintTo<boost::filesystem::path> (container=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:287

[...many more repetitions of these lines..]

#116387 0x00000000004c237d in 
testing::internal::UniversalPrint<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:756
#116388 0x00000000004c3d37 in 
testing::internal::DefaultPrintTo<boost::filesystem::path> (container=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:287
#116389 0x00000000004c3846 in 
testing::internal::PrintTo<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:376
#116390 0x00000000004c2ea6 in 
testing::internal::UniversalPrinter<boost::filesystem::path>::Print (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:600
#116391 0x00000000004c237d in 
testing::internal::UniversalPrint<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:756
#116392 0x00000000004c3d37 in 
testing::internal::DefaultPrintTo<boost::filesystem::path> (container=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:287
#116393 0x00000000004c3846 in 
testing::internal::PrintTo<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:376
#116394 0x00000000004c2ea6 in 
testing::internal::UniversalPrinter<boost::filesystem::path>::Print (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:600
#116395 0x00000000004c237d in 
testing::internal::UniversalPrint<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:756
#116396 0x00000000004c3d37 in 
testing::internal::DefaultPrintTo<boost::filesystem::path> (container=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:287
#116397 0x00000000004c3846 in 
testing::internal::PrintTo<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:376
#116398 0x00000000004c2ea6 in 
testing::internal::UniversalPrinter<boost::filesystem::path>::Print (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:600
#116399 0x00000000004c237d in 
testing::internal::UniversalPrint<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:756
#116400 0x00000000004c3d37 in 
testing::internal::DefaultPrintTo<boost::filesystem::path> (container=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:287
#116401 0x00000000004c3846 in 
testing::internal::PrintTo<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:376
#116402 0x00000000004c2ea6 in 
testing::internal::UniversalPrinter<boost::filesystem::path>::Print (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:600
#116403 0x00000000004c237d in 
testing::internal::UniversalPrint<boost::filesystem::path> (value=..., 
os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:756
#116404 0x00000000004c22d0 in 
testing::internal::UniversalPrinter<boost::filesystem::path const&>::Print 
(value=..., os=0x7fffffffd720) at 
/home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/
gtest-printers.h:668
#116405 0x00000000004c0d81 in 
testing::internal::TuplePrefixPrinter<1ul>::PrintPrefixTo<std::tuple<boost::file
system::path const&> > (t=std::tuple containing = {...}, os=0x7fffffffd720)
    at /home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/gtest-printers.h:810
#116406 0x00000000004bfe71 in 
testing::internal::PrintTupleTo<std::tuple<boost::filesystem::path const&> > 
(t=std::tuple containing = {...}, os=0x7fffffffd720)
    at /home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/gtest-printers.h:827
#116407 0x00000000004bd1e5 in 
testing::internal::PrintTo<boost::filesystem::path const&> (t=std::tuple 
containing = {...}, os=0x7fffffffd720)
    at /home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/gtest-printers.h:503
#116408 0x00000000004b8eaa in 
testing::internal::UniversalPrinter<std::tuple<boost::filesystem::path const&> 
>::Print (value=std::tuple containing = {...}, os=0x7fffffffd720)
    at /home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/gtest-printers.h:600
#116409 0x00000000004b66a6 in 
testing::internal::UniversalPrint<std::tuple<boost::filesystem::path const&> > 
(value=std::tuple containing = {...}, os=0x7fffffffd720)
    at /home/heinzi/workspace_cpp/cryfs/build.debug/test/gtest/src/gtest/include/gtest/gtest-printers.h:756
#116410 0x00000000004b4e8b in testing::internal::FunctionMockerBase<void 
(boost::filesystem::path const&)>::UntypedDescribeUninterestingCall(void 
const*, std::ostream*) const (this=0x7fffffffd950, untyped_args=0x7fffffffd8f0, 
    os=0x7fffffffd720) at /home/heinzi/workspace_cpp/cryfs/build.debug/test/gmock/src/gmock/include/gmock/gmock-spec-builders.h:1604
#116411 0x000000000050204d in 
testing::internal::UntypedFunctionMockerBase::UntypedInvokeWith(void const*) ()
#116412 0x00000000004af8af in testing::internal::FunctionMockerBase<void 
(boost::filesystem::path 
const&)>::InvokeWith(std::tuple<boost::filesystem::path const&> const&) 
(this=0x7fffffffd950, args=std::tuple containing = {...})
    at /home/heinzi/workspace_cpp/cryfs/build.debug/test/gmock/src/gmock/include/gmock/gmock-spec-builders.h:1529
#116413 0x00000000004ad301 in testing::internal::FunctionMocker<void 
(boost::filesystem::path const&)>::Invoke(boost::filesystem::path const&) 
(this=0x7fffffffd950, a1=...)
    at /home/heinzi/workspace_cpp/cryfs/build.debug/test/gmock/src/gmock/include/gmock/gmock-generated-function-mockers.h:97
#116414 0x00000000004c8d7f in MyMock::func (this=0x7fffffffd950, gmock_a1=...) 
at /home/heinzi/workspace_cpp/cryfs/src/test/fspp/test.cpp:7
---Type <return> to continue, or q <return> to quit---
#116415 0x00000000004c8b84 in MyTest_mockTest_Test::TestBody (this=0x75bd10) at 
/home/heinzi/workspace_cpp/cryfs/src/test/fspp/test.cpp:12
#116416 0x00000000004f34a2 in void 
testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, 
void>(testing::Test*, void (testing::Test::*)(), char const*) ()
#116417 0x00000000004ee2b7 in void 
testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, 
void>(testing::Test*, void (testing::Test::*)(), char const*) ()
#116418 0x00000000004d429b in testing::Test::Run() ()
#116419 0x00000000004d4b1b in testing::TestInfo::Run() ()
#116420 0x00000000004d51f0 in testing::TestCase::Run() ()
#116421 0x00000000004dbe42 in testing::internal::UnitTestImpl::RunAllTests() ()
#116422 0x00000000004f4a65 in bool 
testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::Uni
tTestImpl, bool>(testing::internal::UnitTestImpl*, bool 
(testing::internal::UnitTestImpl::*)(), char const*) ()
#116423 0x00000000004ef14f in bool 
testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTe
stImpl, bool>(testing::internal::UnitTestImpl*, bool 
(testing::internal::UnitTestImpl::*)(), char const*) ()
#116424 0x00000000004da96e in testing::UnitTest::Run() ()
#116425 0x00000000004fe856 in RUN_ALL_TESTS() ()
#116426 0x00000000004fe7f1 in main ()

Original issue reported on code.google.com by [email protected] on 18 Nov 2014 at 6:27

_Copied from original issue: google/googlemock#170_

Priority-Medium Type-Defect auto-migrated

Most helpful comment

_From @GoogleCodeExporter on August 24, 2015 22:40_

This bug can also be triggered in Visual Studio 2012 with boost 1.51 gmock 
1.7.0.

You can work around this bug if you define a PrintTo function. Defining a 
PrintTo function is described in 
https://code.google.com/p/googletest/wiki/AdvancedGuide#Teaching_Google_Test_How
_to_Print_Your_Values

I use the following PrintTo function:

namespace boost {
    namespace filesystem {
        void PrintTo(const boost::filesystem::path& path, std::ostream* os) {
            *os << path;
        }
    }
}

Original comment by [email protected] on 20 May 2015 at 1:32

All 13 comments

_From @GoogleCodeExporter on August 24, 2015 22:40_

btw I'm using libboost-filesystem in version 1.55

Original comment by [email protected] on 18 Nov 2014 at 6:29

_From @GoogleCodeExporter on August 24, 2015 22:40_

Can you make an even more minimal example that doesn't depend on Boost?
I'd like to know what it is about boost::filesystem::path that makes this 
happen.

Original comment by [email protected] on 18 Nov 2014 at 6:33

_From @GoogleCodeExporter on August 24, 2015 22:40_

I tried to, but gave up after some hours. It is quite complicated to resolve 
all the inner boost dependencies, boost::filesystem::path is having.

Original comment by [email protected] on 18 Nov 2014 at 8:12

_From @GoogleCodeExporter on August 24, 2015 22:40_

I have this bug too.
The problem in function DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), 
value, os); [gmock-printers.h]. This function calls infinity times, because it 
desides that boost::filesystem::path is container (has cons_iterator), then 
takes first element: it = container.begin(), and then calls DefaultPrintTo(..., 
*it, ) again (subsequented call) - and we have infifnity calls loop, because 
the value_type of path iterator is - path !!!! (we have infinity nested 
containers)

Original comment by [email protected] on 23 Apr 2015 at 3:29

_From @GoogleCodeExporter on August 24, 2015 22:40_

This bug can also be triggered in Visual Studio 2012 with boost 1.51 gmock 
1.7.0.

You can work around this bug if you define a PrintTo function. Defining a 
PrintTo function is described in 
https://code.google.com/p/googletest/wiki/AdvancedGuide#Teaching_Google_Test_How
_to_Print_Your_Values

I use the following PrintTo function:

namespace boost {
    namespace filesystem {
        void PrintTo(const boost::filesystem::path& path, std::ostream* os) {
            *os << path;
        }
    }
}

Original comment by [email protected] on 20 May 2015 at 1:32

It seems that the root of the problem is the incorrect assumption that IsContainerTest is only interested whether template parameter C is a "Container", but actually it should discriminate "RecursiveContainer" and "NonRecursiveContainer", where only the second one should be directed into DefaultPrintTo(IsContainer, ..). IMO the first category should simply defer to operator<<; this would solve the problem for any filesystem::path type or other recursive containers.

I once implemented a trait like this even with C++03 restrictions. Given the declarations within <gtest/gtest.h> I could imagine to create such a MP facility based on the existing Types tuple-like template plus the following additional n-ary MP constructs: contains, or_, and concat.

What do you think of such an approach?

Sorry I don't get it.

On Sunday, December 20, 2015, Daniel Krügler [email protected]
wrote:

It seems that the root of the problem is the incorrect assumption that
IsContainerTest is only interested whether template parameter C is a
"Container", but actually it should discriminate "RecursiveContainer" and
"NonRecursiveContainer",

Meaning what?

where only the second one should be directed into DefaultPrintTo(IsContainer,
..). IMO the first category should simply defer to operator<<; this would
solve the problem for any filesystem::path type or other recursive
containers.

I once implemented a trait like this even with C++03 restrictions.

I'd have to see that.
I thought we need Decltype to detect operators.

Given the declarations within I could imagine to create
such a MP facility based on the existing Types tuple-like template plus
the following additional n-ary MP constructs: contains, or_, and concat.

What do you think of such an approach?

—
Reply to this email directly or view it on GitHub
https://github.com/google/googletest/issues/521#issuecomment-166122022.

In the context of this issue I would define a "RecursiveContainer" as a type that is a "Container" (as defined by the simple definition provided by the IsContainerTest function) that has the property that it has a value type (Defined by std::iterator_traits<typename C::iterator>::value_type, since as a "Container" it is guaranteed to have C::iterator [see IsContainerTest]), that is equal to one of it's "parent" container types. In other words: While searching through the chain of "value types" create a type list of these until you either reach an end (not recursive) or you find another value type that is already contained in the list of value types (recursive).

For example, both boost::filesystem::path as well as std::experimental::filesystem::path are recursive containers C, because std::iterator_traits<C::iterator>::value_type is equal to C. Same thing for std::vector<boost::filesystem::path> (Here at the second level, not at the first one) or for a possible user-defined type similar to the following one:

struct Funny
{
  struct Other
  {
    typedef std::list<Funny>::const_iterator iterator;
    typedef iterator const_iterator;
  };
  typedef std::vector<Other>::const_iterator iterator;
  typedef iterator const_iterator;
};

I don't understand your assertion regarding operators and decltype. First, there are non-SFINAE ways that work even with old compilers to deduce the existence of an available operator<< overload given the tested argument types. But in this case I would simply create a compile-error (instead of a runtime stack overflow), if no PrintTo function has been selected. Instead of deciding for "Container" and select (unconditionally) DefaultPrintTo(IsContainer, ..), just introduce a further splitting into DefaultPrintTo(IsNonRecursiveContainer, ..) and DefaultPrintTo(IsRecursiveContainer, ..), where the latter one doesn't go recursively through the chain of internal::UniversalPrint calls. I'm aware that this is just a sketch and possibly not easy to understand without looking at a concrete implementation, but I hope I have roughly clarified what kind of source changes would be necessary. If not, please try to ask more specific question (but be aware that I have not written the full solution yet).

The most critical decision would be (IMO) whether the increase of complexity to determine a "RecursiveContainerType", depending on something like the existing Types tuple-like type and the above mentioned MT helpers would seem acceptable to realize the goal. If so, I would try to make a more concrete suggestion (Presumably not before start of the next year, but the mentioned problem is an old and nasty one, so I hope the additional time needed to see something is worth waiting for it).

On Sun, Dec 20, 2015 at 10:43 AM, Daniel Krügler [email protected]
wrote:

In the context of this issue I would define a "RecursiveContainer" as a
type that is a "Container" (as defined by the simple definition provided by
the IsContainerTest function) that has the property that it has a value
type (Defined by std::iterator_traits::value_type,
since as a "Container" it is guaranteed to have C::iterator [see
IsContainerTest]), that is equal to one of it's "parent" container types.
In other words: While searching through the chain of "value types" create a
type list of these until you either reach an end (not recursive) or you
find another value type that is already contained in the list of value
types (recursive).

OH! I didn't know we had such beasts.

One quick question: is it possible for such a detection to not terminate?
That is, are there containers for which the nested value types get more
complex the more you dig in a way that doesn't repeat? It seems like it
might be possible.

For example, both boost::filesystem::path as well as
std::experimental::filesystem::path are recursive containers C, because
std::iterator_traits::value_type is equal to C. Same thing
for std::vectorboost::filesystem::path (Here at the second level, not
at the first one) or for a possible user-defined type similar to the
following one:

struct Funny
{
struct Other
{
typedef std::list::const_iterator iterator;
typedef iterator const_iterator;
};
typedef std::vector::const_iterator iterator;
typedef iterator const_iterator;
};

I don't understand your assertion regarding operators and decltype.

First, there are non-SFINAE ways that work even with old compilers to
deduce the existence of an available operator<< overload given the tested
argument types.

It sounds like you understand me and you think I'm wrong. That's a little
different. :)
I don't think I know how to do the kind of deduction you're suggesting, but
I'd like to see it.

But in this case I would simply create a compile-error (instead of a
runtime stack overflow), if no PrintTo function has been selected.
Instead of deciding for "Container" and select (unconditionally) DefaultPrintTo(IsContainer,
..), just introduce a further splitting into DefaultPrintTo(IsNonRecursiveContainer,
..) and DefaultPrintTo(IsRecursiveContainer, ..), where the latter one
doesn't go recursively through the chain of internal::UniversalPrint
calls. I'm aware that this is just a sketch and possibly not easy to
understand without looking at a concrete implementation, but I hope I have
roughly clarified what kind of source changes would be necessary. If not ,
please try to ask more specific question (but be aware that I have not
written the full solution yet).

The most critical decision would be (IMO) whether the increase of
complexity to determine a "RecursiveContainerType", depending on something
like the existing Types tuple-like type and the above mentioned MT
helpers would seem acceptable to realize the goal. If so, I would try to
make a more concrete suggestion (Presumably not before start of the next
year, but the mentioned problem is an old and nasty one, so I hope the
additional time needed to see something is worth waiting for it).

ok.

Considering that most compilers have now implemented <experimental/filesytem>, signifying that containers with recursive versions of themselves as iterators are now a part of the standard library, it would be a good point to re-reference this issue.

Using gcc or clang, I can reproduce this with a simple file. Save the following code snippet to a.cpp:

#include <experimental/filesystem>
#include <gtest/gtest.h>

namespace fs = std::experimental::filesystem;


int main()
{
    fs::path p1("/lib/libc.so.6");
    fs::path p2("/lib/libnsl.so");
    EXPECT_TRUE(p1 == p2);      // fine
    EXPECT_EQ(p1, p2);          // infinite loop

    return 0;
}

Then, compile and run:

$ g++ a.cpp -lstdc++fs -gtest
$ ./a.out 
a.cpp:15: Failure
Value of: p1 == p2
  Actual: false
Expected: true
Segmentation fault (core dumped)

I'm not sure what the best solution would be (maybe SFINAE detection if the std::is_same<std::iterator_traits<T>::value_type, T>, with a specialized handler or throw a compile-time error), but I feel this issue should at least be considered...

Yes, sorry, my intention was to work on that issue myself, but I lost the track of it. Thanks for the reminder!

I prepared a pull request (Prevent infinite loops for recursive containers like boost::filesystem::path #1186) which has passed all travis tests and I signed the CLA. The solution is simpler than I originally thought: The suggested fix simply prevents "recursive containers" entering the normal container output specialization. The effect is that boost::filesystem::path and similar containers use their own operator<< overload, if that exists, otherwise the "binary output fallback" will be used.

For example,

boost::filesystem::path p("k:\\some-thing\\weird");

would output (including quotation marks):

"k:\some-thing\weird"

Since I'm not aware of other know "recursively-defined" containers than boost::filesystem::path and std::filesystem::path (which both define their own operator<< overload), I see currently no reason for further fine-tuning the solution until another use-case occurs. Please let me know, if there are other types to consider as well, where the suggested approach would not work.

1186 should have addressed this

Was this page helpful?
0 / 5 - 0 ratings

Related issues

apivovarov picture apivovarov  Â·  3Comments

Joebeazelman picture Joebeazelman  Â·  5Comments

agirault picture agirault  Â·  5Comments

pepe82sh picture pepe82sh  Â·  5Comments

GoogleCodeExporter picture GoogleCodeExporter  Â·  5Comments