以chrono获取时间类型的名称

Get the name of a time type in chrono

本文关键字:类型 取时间 chrono 获取      更新时间:2023-10-16

假设我有一个时间测量类,可以通过像这样的持续时间类型进行参数化

template<typename TimeT = std::chrono::milliseconds>
struct measure
{ /* implementation */ };

我想要的是能够打印出TimeT。我倾向于实现这样的静态成员函数:

static string TimeType() const; 

我的问题是:

  1. 我应该增加一名成员吗?这不应该是静态的吗
  2. 应该如何实现这一点我应该使用依赖于实现的、非编译时的typeinfo / name组合(在这种情况下,我必须删除上面的constexpr),还是应该选择创建几个专门化,以便每个时间类型返回正确的字符串
  3. 有没有更标准/更惯用的方法来获取时间类型的名称

欢迎您使用我的<chrono_io>库。它由一个标题"chrono_io.h"组成,该标题在文档中链接到。示例用途为:

#include "chrono_io.h"
#include <iostream>
template<typename TimeT = std::chrono::milliseconds>
struct measure
{
    TimeT value;
    friend
    std::ostream&
    operator<< (std::ostream& os, const measure& m)
    {
        using namespace date;
        return os << m.value;
    }
};
int
main()
{
    using namespace std::chrono;
    measure<> m1 = {30ms};
    std::cout << m1 << 'n';
    measure<duration<int, std::ratio<1, 60>>> m2 = {duration<int, std::ratio<1, 60>>{45}};
    std::cout << m2 << 'n';
}

输出:

30ms
45[1/60]s

实现这一点的一种方法是对时间类型进行专门化;这样typeid.name()的不可移植性就不再是一个因素:

/// get the name of the chrono time type
template<typename T> string time_type()                  { return "unknown";      }
template<> string time_type<std::chrono::nanoseconds >() { return "nanoseconds";  }
template<> string time_type<std::chrono::microseconds>() { return "microseconds"; }
template<> string time_type<std::chrono::milliseconds>() { return "milliseconds"; }
template<> string time_type<std::chrono::seconds     >() { return "seconds";      }
template<> string time_type<std::chrono::minutes     >() { return "minutes";      }
template<> string time_type<std::chrono::hours       >() { return "hours";        }

这个q没有得到太多关注。我发布了一个答案作为代码质量比较的最小基础

当然,这里的困难情况是,在编译时有这些信息,这将需要编译时字符串n’填充

上述的另一个版本是

template<class> struct time_type          { constexpr static char const *name = "unknown";      };
template<> struct time_type<nanoseconds > { constexpr static char const *name = "nanoseconds";  };
template<> struct time_type<microseconds> { constexpr static char const *name = "microseconds"; };
template<> struct time_type<milliseconds> { constexpr static char const *name = "milliseconds"; };
template<> struct time_type<seconds     > { constexpr static char const *name = "seconds";      };
template<> struct time_type<minutes     > { constexpr static char const *name = "minutes";      };
template<> struct time_type<hours       > { constexpr static char const *name = "hours";        };

durationtime_point都有一个类型为ratioperiod成员,该成员表示该类型的每个刻度的秒数。

我不相信C++标准提供了一种将10的特殊幂转换为度量前缀的方法;为此,您必须推出自己的功能。但要注意,这个时期不一定是任何标准的时期;用户可以根据他们喜欢的任何周期(例如年)甚至标准周期创建时间测量,但使用不同的表示类型(例如double),所以你可能不应该假设。为什么不让它成为另一个参数呢?

您试图做的是反射定义的一部分,更具体地说是运行时类型信息(RTTI),它使代码能够在运行时自行检查。不久前,当我试图制作一个系统,将传入类型的所有成员变量串行化为{ "varName"=<varValue> } 等格式时,我对此进行了深入研究

不幸的是,简单的答案是C++本身并不支持反射,在它提供"类似"功能(即<type_traits>typeid())的地方,还有很多不足之处。最好的结果将来自生成您需要的数据的自定义构建步骤,我决定反对这种方法,所以在这种情况下,不能真正帮助"如何",但这些方法显然在可移植性方面受到限制。

然而,您已经提到的模板专业化方法是我所见过的C++运行时反射的最佳实现的一步,它使用宏的组合来生成类描述符。

MFC宏DECLARE_DDYNAMIC和IMPLEMENT_DYNAMIC是为类创建其中一个描述符所需的所有污染,然后添加函数IsKindOf()GetRuntimeClass()等,最有用的通常是返回CRuntimeClassGetRuntimeClass()

为了实现类似的东西,你可以使用类似的东西;

#define MakeClassDescriptor(classname) 
private: 
class classname ## Descriptor 
{ 
public: 
    static const char* GetName() { return #classname; } 
}; 
public: 
    typedef classname ## Descriptor ClassDescriptor;
class Test
{
public:
    Test() {};
    MakeClassDescriptor(Test);
};

这将允许您通过Test::ClassDescriptor::GetName() 访问该名称

当然,还有其他方法和您自己的规范规定了如何实现这一点,例如,类从模板化RTTI类继承的方法,请参阅Dominic Filion在《游戏编程宝石5 》一书中撰写的文章"在C++中使用反射模板"