универсальный std-подобный хроно-независимый шаблон секундомера

#c #c 11 #templates #generic-programming

#c #c 11 #шаблоны #универсальный-программирование

Вопрос:

У меня был простой класс секундомера, который в основном использовался std::chrono::steady_clock::now() - this->start_ в его реализации для измерения прошедшего времени. Я решил обновить его и сделать полностью универсальным, совместимым с std, не зависящим от конкретного типа часов, чтобы пользователь мог указать свой собственный тип часов. У меня также был шаблон stopwatch::operator DurationType() const для удобства, например, для преобразования в определенный тип длительности:

 std::chrono::milliseconds msecs = stopwatch;
 

Но есть одна проблема: я не могу избавиться от chrono заголовка, поскольку я использую его duration_cast .

У меня есть 2 идеи. Один из них работает, но не очень удобен, а другой вообще не работает и довольно уродлив.

 // stopwatch.h
#pragma once

// #include <chrono>

namespace eld
{
    template <typename ClockType,
            typename DurationCaster>
    class stopwatch
    {
    public:

        using clock_type = ClockType;
        using duration = typename clock_type::duration;

        stopwatch()
             : start_(clock_type::now())
        {}

        void reset()
        {
            start_ = clock_type::now();
        }

        typename clock_type::duration result() const
        {
            return clock_type::now() - start_;
        }

        template<typename Duration>
        Duration result() const
        {
            // return std::chrono::duration_cast<Duration>(result()); // TODO: remove this
            return DurationCaster()(Duration(), result());
        }

        template<typename Duration>
        operator Duration() const
        {
            return result<Duration>();
        }

        // This is ugly and does not work
//        template <typename Caster>
//        auto operator()(Caster caster) -> decltype(Caster::operator()(typename clock_type::time_point))
//        {
//            return caster(result());
//        }

    private:
        typename clock_type::time_point start_;
    };

}

 
 // dummy.cpp

#include <stopwatch/stopwatch.h>

#include <iostream>
#include <chrono>
#include <thread>

// this is inconvenient
struct DurationCaster
{
    template<typename ToDuration, typename FromDuration>
    constexpr ToDuration operator()(ToDuration, FromDuration fromDuration) const
    {
        return std::chrono::duration_cast<ToDuration>(fromDuration);
    }
};

int main()
{

    eld::stopwatch<std::chrono::steady_clock, DurationCaster> steadyWatch{};

    std::this_thread::sleep_for(std::chrono::milliseconds(20));

    std::chrono::nanoseconds nanoseconds = steadyWatch.result();
    std::cout << nanoseconds.count() << std::endl;

    std::chrono::milliseconds milliseconds = steadyWatch;
    std::cout << milliseconds.count() << std::endl;

    // This is ugly and does not work
//    milliseconds = steadyWatch([](typename decltype(steadyWatch)::duration from)
//            {
//        return std::chrono::duration_cast<std::chrono::milliseconds>(from);
//            });

    return 0;
}
 

Хотя первый вариант работает, несмотря на его неудобства, я хочу знать, могу ли я избавиться от std::chrono::duration_cast в моем заголовке более элегантным способом.
Или это так хорошо, как получается?

PS: Что не так с моим вторым подходом? Почему я не могу его скомпилировать?

Ответ №1:

Если вы хотите использовать первый подход, может быть, передать DurationCaster в качестве аргумента конструктору, тогда можно будет использовать функции, а не только функциональные объекты? А также вам не нужно создавать DurationCaster каждый раз operator() , сделайте это один раз.
Или вы можете использовать default DurationCaster , который использует duration_cast under the hood , define как typename DurationCaster = your_default_caster

О том, почему не работает вторая: как я вижу, здесь ошибка в decltype(Caster::operator()(typename clock_type::time_point)) том, operator что вы называете статической функцией-членом, это неправильно.

Это должно сработать:

 template <typename Caster>
auto operator()(Caster caster) -> typename std::result_of<Caster(typename clock_type::duration)>::type
{
    return caster(result());
}
 

PS: я знаю result_of , что он устарел, но вопрос касается cpp11