获取 chrono 中时间类型的名称

Get the name of a time type in chrono

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

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

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

static string TimeType() const; 

我的问题是:

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

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

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

一种方法是专注于时间类型;这样 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";        }

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

当然,这里的困难情况是在编译时获得此信息,这需要 compile time strings n' stuff

上面的另一个版本是

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";        };

欢迎您使用我的<chrono_io> library。它由文档中链接的单个 header、"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

你试图做的是 reflection, more specifically run time type information (RTTI) 的定义的一部分,它使代码能够在运行时自行检查,我在尝试制作时仔细研究了这一点一个将传入类型的所有成员变量序列化为 { "varName"=<varValue> }

格式的系统

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

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

MFC 宏DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC are all the pollution you need to create one of these descriptors for your class, which then add the functions IsKindOf(), GetRuntimeClass(), etc., the most useful often being GetRuntimeClass() which returns a CRuntimeClass

要实现类似的功能,您可以使用类似的东西;

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

访问该名称

当然还有其他方法,您自己的规范决定了如何实现它,例如 classes 继承自模板化 RTTI class 查看文章 "Using Templates for Reflection in C++" Dominic Filion 在《游戏编程精粹 5》一书中写的