trompeloeil: Segmentation fault / infinite loop in regular expression when handling fmt::is_formattable types

So this is weird, but in

#include <fmt/format.h>
#include <trompeloeil.hpp>
#include <type_traits>

#if 1
namespace trompeloeil {

template<typename T>
struct printer<T, typename std::enable_if<fmt::is_formattable<T>::value>::type>
{
    static void print(std::ostream& os, const T& t) noexcept { os << fmt::format("{}", t); }
};

}
#endif

class mock
{
  public:
    MAKE_MOCK1(function_call, void(const std::string& text));
};

int main() {
	mock test;
	REQUIRE_CALL_V(test, function_call(trompeloeil::re("A")));
#if 0
	test.function_call("A");
#else
	test.function_call("B");
#endif
}

I get an infinite loop (optimized build) or segmentation fault with a backtrace with a loooooong recursion of trompeloeil::duck_typed_matcher<trompeloeil::lambdas::regex_check, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::operator char const*&&<char const*, void, bool>() const ( this=0x4632b8) at trompeloeil/include/trompeloeil.hpp:1151 (debug build) as long as those #if are as they are. If you remove the printer, or make the regular expression match, the problem goes away.

The problem happens with both gcc 11.2 and clang 13.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 19 (10 by maintainers)

Most upvoted comments

I think fmt otherwise concludes that trompeloeil::re is printable as a C-string, since it wants to instantiate the conversion to const char* operator, which triggers the infinite recursion.

* Any change in behaviour if the body of `print(std::ostream&, const T&)` is commented out?

Yes, that makes it work. As does using fmt::print(os, "{}", t); (after including fmt/ostream.h) instead of os << fmt::format("{}", t);

About versions, it’s easy to use the latest ones, specially if you go for header-only

$ git clone git@github.com:fmtlib/fmt.git
Cloning into 'fmt'...
remote: Enumerating objects: 28566, done.
remote: Counting objects: 100% (2368/2368), done.
remote: Compressing objects: 100% (242/242), done.
remote: Total 28566 (delta 2226), reused 2125 (delta 2103), pack-reused 26198
Receiving objects: 100% (28566/28566), 13.67 MiB | 5.15 MiB/s, done.
Resolving deltas: 100% (19317/19317), done.
$ git clone git@github.com:rollbear/trompeloeil.git
Cloning into 'trompeloeil'...
remote: Enumerating objects: 4838, done.
remote: Counting objects: 100% (606/606), done.
remote: Compressing objects: 100% (314/314), done.
remote: Total 4838 (delta 346), reused 430 (delta 202), pack-reused 4232
Receiving objects: 100% (4838/4838), 2.43 MiB | 811.00 KiB/s, done.
Resolving deltas: 100% (3280/3280), done.
$ g++ -o test test.cpp -Ifmt/include/ -Itrompeloeil/include -DFMT_HEADER_ONLY
$ ./test 
Segmentation fault (core dumped)