Подтвердить что ты не робот

Преобразовать std:: chrono:: system_clock:: time_point в struct timeval и обратно

Я пишу код на С++, который должен получить доступ к старой библиотеке C, которая использует timeval в качестве представления текущего времени.

В старом пакете для получения текущей даты/времени мы использовали:

struct timeval dateTime;
gettimeofday(&dateTime, NULL);

function(dateTime); // The function will do its task

Теперь мне нужно использовать С++ chrono, что-то вроде:

    system_clock::time_point now = system_clock::now();
    struct timeval dateTime;

    dateTime.tv_sec = ???? // Help appreaciated here
    dateTime.tv_usec = ???? // Help appreaciated here

    function(dateTime);

Позже в коде мне нужно вернуться назад, построив переменную time_point из возвращаемого struct timeval:

    struct timeval dateTime;
    function(&dateTime);

    system_clock::time_point returnedDateTime = ?? // Help appreacited

Я использую С++ 11.

4b9b3361

Ответ 1

[Отредактировано использование time_val вместо бесплатных vars]

Предполагая, что вы доверяете system_clock с точностью до миллисекунды, вы можете сделать следующее:

  struct timeval dest;
  auto now=std::chrono::system_clock::now();
  auto millisecs=
    std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()
    );;
  dest.tv_sec=millisecs.count()/1000;
  dest.tv_usec=(millisecs.count()%1000)*1000;

  std::cout << "s:" << dest.tv_sec << " usec:" << dest.tv_usec << std::endl;

Используйте std::chrono::microseconds в duration_cast и соответствующим образом настройте свой код (div/mod) для более высокой точности - будьте осторожны, насколько вы доверяете точности полученных вами значений.

Обратный возврат:

  timeval src;

  // again, trusting the value with only milliseconds accuracy
  using dest_timepoint_type=std::chrono::time_point<
    std::chrono::system_clock, std::chrono::milliseconds
  >;
  dest_timepoint_type converted{
    std::chrono::milliseconds{
      src.tv_sec*1000+src.tv_usec/1000
    }
  };

  // this is to make sure the converted timepoint is indistinguishable by one
  // issued by the system_clock
  std::chrono::system_clock::time_point recovered =
      std::chrono::time_point_cast<std::chrono::system_clock::duration>(converted)
  ;

Ответ 2

См. std::chrono::system_clock::to_time_t(), который преобразует time_point в time_t, который становится вашим tv_sec. Вы не получаете tv_usec, вы можете установить его 0; или вы могли бы поиграть с несколькими другими вещами, включая duration_cast, чтобы извлечь доли секунды из вашего time_point.

from_time_t() делает обратное.

Ответ 3

Вот как сделать преобразование без использования ручных коэффициентов преобразования или в зависимости от неуказанного режима округления time_t:

timeval
to_timeval(std::chrono::system_clock::time_point tp)
{
    using namespace std::chrono;
    auto s = time_point_cast<seconds>(tp);
    if (s > tp)
        s = s - seconds{1};
    auto us = duration_cast<microseconds>(tp - s);
    timeval tv;
    tv.tv_sec = s.time_since_epoch().count();
    tv.tv_usec = us.count();
    return tv;
}

std::chrono::system_clock::time_point
to_time_point(timeval tv)
{
    using namespace std::chrono;
    return system_clock::time_point{seconds{tv.tv_sec} + microseconds{tv.tv_usec}};
}

to_timeval заботится о том, чтобы округлить tp вниз (в случае, если он отрицательный). Спецификация POSIX немного расплывчата в этом, но я предполагаю, что timeval представляет моменты времени до эпохи с отрицательными значениями tv_sec, а затем положительные значения tv_usec. Тогда это простая операция, чтобы найти microseconds с последнего second.

Если я ошибаюсь в своем предположении (и можно найти более точную спецификацию POSIX), <chrono> имеет право моделировать все, что он делает.

Обратное преобразование, предполагая вышеприведенные соглашения, невероятно читаемо. Это не требует комментариев.

Все это можно протестировать следующим образом:

timeval
make_timeval(time_t s, long us)
{
    timeval tv;
    tv.tv_sec = s;
    tv.tv_usec = us;
    return tv;
}

bool
operator==(timeval x, timeval y)
{
    return x.tv_sec == y.tv_sec && x.tv_usec == y.tv_usec;
}

int
main()
{
    using namespace std::chrono;
    assert(make_timeval(0, 0) == to_timeval(system_clock::time_point{}));
    assert(make_timeval(1, 0) == to_timeval(system_clock::time_point{seconds{1}}));
    assert(make_timeval(1, 400000) == to_timeval(system_clock::time_point{seconds{1} + microseconds{400000}}));
    assert(make_timeval(-1, 400000) == to_timeval(system_clock::time_point{seconds{-1} + microseconds{400000}}));

    assert(to_time_point(make_timeval(0, 0)) == system_clock::time_point{});
    assert(to_time_point(make_timeval(1, 0)) == system_clock::time_point{seconds{1}});
    assert(to_time_point(make_timeval(1, 400000)) == system_clock::time_point{seconds{1} + microseconds{400000}});
    assert(to_time_point(make_timeval(-1, 400000)) == system_clock::time_point{seconds{-1} + microseconds{400000}});
}

Все это основывается на предположении, что эпоха для timeval и system_clock идентична. Это не указано, но верно для всех существующих реализаций. В любом случае, мы можем стандартизировать эту существующую практику в ближайшем будущем.

Помните, что в POSIX timeval используется как time_point, так и duration. Таким образом, to_time_point может привести к ошибке времени выполнения, если timeval в настоящее время представляет продолжительность времени. И to_timeval может привести к ошибке времени выполнения, если клиент интерпретирует результат как продолжительность времени.