Как напечатать std :: chrono types с помощью Google test?

#c #googletest

#c #googletest

Вопрос:

Я пытаюсь использовать std::chrono типы с помощью Google test. Мой первый подход заключается в определении PrintTo for nanoseconds в пространстве имен std::chrono , но, к сожалению , добавление объявлений или определений в пространство имен std или в любое пространство имен, вложенное в std . Следующий код демонстрирует идею.

 #include <gtest/gtest.h>
#include <chrono>

namespace std::chrono {

void PrintTo(nanoseconds ns, std::ostream* os) // UB
{
    *os << ns.count() << " nanoseconds ";
}

}

namespace {

struct MyTest : ::testing::Test{
};

TEST_F(MyTest, PrintingTest)
{
    using namespace testing;
    using namespace std::chrono_literals;
    ASSERT_THAT(1ns, Eq(2ns));
}

}
  

Если std::chrono::PrintTo определено, оно печатает:

 Value of: 1ns
Expected: is equal to 2 nanoseconds 
  Actual: 
  

Если std::chrono::PrintTo не определено, оно печатается через байтовый принтер по умолчанию:

 Value of: 1ns
Expected: is equal to 8-byte object <02-00 00-00 00-00 00-00>
  Actual: 
  

Каков идиоматический способ определения принтера для std::chrono типов с помощью Google test?

Комментарии:

1. C 20-х operator<<() годов решит проблему.

Ответ №1:

Вы можете перегрузить оператор std::ostream для типов chrono следующим образом:

 #include <gtest/gtest.h>
#include <chrono>

std::ostreamamp; operator<<(std::ostreamamp; os, const ::std::chrono::nanosecondsamp; ns) 
{
  return os << ns.count() << " nanoseconds ";
}

namespace {

struct MyTest : ::testing::Test{
};

TEST_F(MyTest, PrintingTest)
{
    using namespace testing;
    using namespace std::chrono_literals;
    ASSERT_EQ(1ns, 2ns);
}

}
  

Тогда результат должен быть таким, как ожидалось:

 error:       Expected: 1ns
      Which is: 1 nanoseconds
To be equal to: 2ns
      Which is: 2 nanoseconds
[  FAILED  ] MyTest.PrintingTest (0 ms)
  

Комментарии:

1. Кстати, это приведет к нарушениям ODR (из-за принтера по умолчанию в Google test UniversalPrinter ), если есть другой cpp-файл, который не определяет operator<<(... std::chrono::nanoseconds) или определяет его по-другому.

2. @DevNull Верно, но тогда ваши варианты — «неопределенное поведение» против «возможного ODR», я не знаю, есть ли другой правильный способ. Я использую реализацию UB (как в вашем вопросе) в своих тестах, я думаю, что в данном случае это приемлемо и правильно определено для текущего STL, в будущем это может стать UB. Возможно, следует задать вопрос на странице gtest github, чтобы попытаться спросить разработчиков, что они рекомендуют

3. Проблема с UB в том, что он может работать, но может и не работать… У меня есть две цели, одна с gcc (7.5) и одна с clang (8.0.1). Это решение отлично работает для первого, но не работает для последнего.

4. @Quarra Возможно, вам потребуется переместить реализацию в пространство имен std:: chrono таким же образом, как в исходном вопросе с помощью PrintTo() . Если это работает для обоих, то было бы неплохо узнать, какой из них правильный, а какой неправильный в данном решении.

5. Но разве расширение пространства имен std само по себе не является UB? en.cppreference.com/w/cpp/language/extending_std