【问题标题】:How to format std::chrono durations?如何格式化 std::chrono 持续时间?
【发布时间】:2023-03-07 23:08:01
【问题描述】:

有没有方便的方法将std::chrono::duration 格式化为指定格式?

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

【问题讨论】:

标签: c++ c++11 string-formatting chrono


【解决方案1】:

这需要一个任意的计时持续时间并将其分解为其他持续时间数量:

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_upget_ratios 待写。

这可以让您获取一个持续时间,并将其格式化为 hh:mm:ss 或其他任何您想要的格式。

【讨论】:

  • 在 C++17 中使用结构化绑定会更好:auto [ss, ms, us] = break_down_durations&lt;std::chrono::seconds, std::chrono::milliseconds, std::chrono::microseconds&gt;( duration );
  • 请注意,break_down_durations 的模板参数必须根据大小正确排序,并且不能有重复。
  • @jotik 是的,这是真的。不正确的命令会让你得到 0,重复会给你编译时错误。
  • @yisu 是的。要移植到 11,请编写索引序列并将基于类型的获取替换为基于索引的获取。
【解决方案2】:

这是使用可变参数模板的可能解决方案。
if constexpr () 使其仅适用于 c++17,但替换为常规 if 仍然有效并且符合 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 formatDuration(DurationIn) { return {}; } // recursion termination

在 main 中测试,输出“77::600::42”

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

【讨论】:

    【解决方案3】:

    这是与@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);
    

    【讨论】:

      【解决方案4】:

      可以使用类似的东西:

      #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());
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-20
        • 2021-01-20
        相关资源
        最近更新 更多