2017-02-09 4 views
4

Есть ли удобный способ форматирования std::chrono::duration в указанном формате?Как форматировать std :: chrono durations?

std::chrono::high_resolution_clock::time_point now, then; 
then = std::chrono::high_resolution_clock::now(); 
// ... 
now = std::chrono::high_resolution_clock::now(); 
auto duration = now - then; 

// base in microseconds: 
auto timeInMicroSec = 
     std::chrono::duration_cast<std::chrono::microseconds>(duration); 

Как я могу форматировать timeInMicroSec как ss::ms::us?

+1

Вы можете посмотреть на [этот вопрос] (http://stackoverflow.com/questions/12835577/how-to-convert-stdchronotime-point-to-calendar-datetime-string-with-fraction) , –

+1

Кроме того, Говард Хиннант предоставил [выдающийся учебник] (https://www.youtube.com/watch?v=P32hvk8b13M) в прошлом году на cppcon. – Jeff

ответ

1

можно использовать что-то вроде:

#include <iomanip> 
#include <sstream> 

//... 

auto c(timeInMicroSec.count()); 
std::ostringstream oss; 
oss << std::setfill('0')   // set field fill character to '0' 
    << (c % 1000000000)/1000000 // format seconds 
    << "::" 
    << std::setw(3)    // set width of milliseconds field 
    << (c % 1000000)/1000  // format milliseconds 
    << "::" 
    << std::setw(3)    // set width of microseconds field 
    << c % 1000;     // format microseconds 
auto formatted(oss.str()); 
7

Это занимает произвольную длительность хронографа и разбивает его на другие продолжительности величин:

template<class...Durations, class DurationIn> 
std::tuple<Durations...> break_down_durations(DurationIn d) { 
    std::tuple<Durations...> retval; 
    using discard=int[]; 
    (void)discard{0,(void((
    (std::get<Durations>(retval) = std::chrono::duration_cast<Durations>(d)), 
    (d -= std::chrono::duration_cast<DurationIn>(std::get<Durations>(retval))) 
)),0)...}; 
    return retval; 
} 

тестового код:

int main() { 
    auto then = std::chrono::high_resolution_clock::now(); 
    std::this_thread::sleep_for(std::chrono::seconds(3)); 
    auto now = std::chrono::high_resolution_clock::now(); 
    auto duration = now - then; 

    auto clean_duration = break_down_durations<std::chrono::seconds, std::chrono::milliseconds, std::chrono::microseconds>(duration); 
    std::cout << std::get<0>(clean_duration).count() << "::" << std::get<1>(clean_duration).count() << "::" << std::get<2>(clean_duration).count() << "\n"; 
} 

код форматирования можно очистить и вставить в функцию.

Live example.

Было бы забавно писать автоформаты для такого набора (возрастающей точности) продолжительности.

Вы должны написать самую длинную продолжительность, затем ::. После этого вы конвертируете одну единицу предыдущей продолжительности в следующую, запишите свой лог на 10 и сделаете setw и выведите следующую продолжительность. Повторяйте, пока не закончите длительность.

Я бы, вероятно, совершил круговое движение через массивы std::size_t как для .count(), так и для отношений.

Как это:

template<class...Durations> 
std::string format_durations(std::tuple<Durations...> d) { 
    std::size_t values[]={(std::size_t)std::get<Durations>(d).count()...}; 
    auto ratios = get_ratios<Durations...>(); 

    std::stringstream ss << std::setfill('0'); 
    ss << values[0]; 

    for (std::size_t const& v:values) { 
    std::size_t i = &v-values; 
    if (i==0) continue; 
    ss << "::" << std::setw(log_10_round_up(ratios[i-1])) << values[i]; 
    } 
    return ss.str(); 
} 

с log_10_round_up и get_ratios быть написаны.

Это позволяет использовать длительность и форматировать ее как hh: mm: ss или что-то еще, что вы хотите.

+2

Еще лучше в C++ 17 со структурированными привязками: 'auto [ss, ms, us] = break_down_durations (длительность); ' – Mael

+0

Обратите внимание, что аргументы шаблона' break_down_durations' должны быть упорядочены по величине и не могут быть дубликатов. – jotik

+0

@jotik да, это правда. Неправильные заказы получают вас 0, дубликаты дают вам ошибки времени компиляции. – Yakk

1

Это возможное решение с использованием вариационного шаблона.
Если constexpr() делает его только C++ 17, но заменяет его регулярным, если он все еще работает и совместим с C++ 14.

template<class DurationIn, class FirstDuration, class...RestDurations> 
std::string formatDuration(DurationIn d) 
{ 
    auto val = std::chrono::duration_cast<FirstDuration>(d); 

    string out = std::to_string(val.count()); 

    if constexpr(sizeof...(RestDurations) > 0) { 
     out += "::" + formatDuration<DurationIn, RestDurations...>(d - val); 
    } 

    return out; 
} 

template<class DurationIn> 
std::string formatDurationRecurse(DurationIn) { return {}; } // recursion termination 

тестирование в основных, выходов "77 :: 600 :: 42"

auto formattedStr = formatDuration< 
    std::chrono::microseconds, 
    std::chrono::seconds, 
    std::chrono::milliseconds, 
    std::chrono::microseconds>(77'600'042us); 
0

Вот такое же решение, как было предложено @jotik, но с использованием в fmt library, что приводит к гораздо более компактный код:

#include <fmt/format.h> 

auto c = timeInMicroSec.count(); 
auto formatted = fmt::format("{}::{:03}::{:03}", 
    (c % 1'000'000'000)/1'000'000, (c % 1'000'000)/1'000, c % 1'000); 
Смежные вопросы