5

Suppose I have a time measuring class, parametrizable by the duration type like this

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

What I want is to be able to print out the TimeT. I'm leaning towards implementing a static member function like this :

static string TimeType() const; 

My questions are :

  1. Should I add a member instead? Should this not be static?
  2. How should the body of this be implemented ? Should I use the implementation dependent, non compile time typeinfo / name combo (in which case I'd have to remove constexpr above) or should I opt for the creation of several specializations that would return the correct string per time type ?
  3. Is there a more standard / idiomatic way of getting the name of the time type ?
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • The standard way to get the "name" of a type, is using the [`type_info`](http://en.cppreference.com/w/cpp/types/type_info) class returned by [`typeid`](http://en.cppreference.com/w/cpp/language/typeid). However, there is nothing in the C++ standard saying *what* the name may be, it may be the compilers mangled name for the type, or something else. – Some programmer dude Mar 22 '15 at 15:41
  • But what I'm more interested in is *why* you would want this? This seems like [an XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – Some programmer dude Mar 22 '15 at 15:42
  • @JoachimPileborg I'm printing a benchmark and just want to put a title in the timing column. I believe since the time type is known at compile time, I shouldn't repeat this info elsewhere (and make room for bugs crawling in). Basically what I do is using [this](http://stackoverflow.com/a/21995693/2567683) from a higher level of abstraction – Nikos Athanasiou Mar 22 '15 at 15:45
  • @JoachimPileborg If you have the time, you could check out [**the project where this was needed**](https://ngathanasiou.wordpress.com/2015/04/01/benchmarking-in-c/). I'd appreciate any remarks. – Nikos Athanasiou Apr 06 '15 at 09:09

4 Answers4

11

You are welcome to use my <chrono_io> library. It consists of a single header, "chrono_io.h" which is linked to in the docs. Example use would be:

#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';
}

which outputs:

30ms
45[1/60]s
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 5
    SO sometimes is great. I mean you post a question and the guy that built the whole thing answers ... cheers H ! – Nikos Athanasiou Mar 25 '15 at 17:42
  • @Howard Hinnant this is indeed an awesome proposal, especially for scientific computing. Any chance the library will make it into C++17? It seems to be already in boost (or was intended to) http://www.boost.org/doc/libs/1_54_0/boost/chrono/chrono_io.hpp – vsoftco Mar 30 '15 at 22:03
  • @vsoftco: Thank you for your comment. I am not sure if I can get it into C++17 or not. And as a public announcement, if anyone else wants to pick this up and run with it on the committee, that would be great too. – Howard Hinnant Mar 31 '15 at 17:34
  • @HowardHinnant Thanks for all your help. [Here](https://ngathanasiou.wordpress.com/2015/04/01/benchmarking-in-c/) is my attempt to build a benchmarking tool based on chrono. I'd appreciate any remarks or corrections. – Nikos Athanasiou Apr 05 '15 at 20:14
7

A way to do this is specializing over the time type; this way non portability of typeid.name() stops being a factor :

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

this q didn't get much attention. I posted an answer as a min base for comparison of code quality

Ofcourse the difficult case here, would be to have this info at compile time which would require compile time strings n' stuff

Another version of the above would be

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";        };
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
2

Both duration and time_point have a period member of type ratio which represents the number of seconds per tick of the type.

I do not believe the C++ standard offers a way of converting special powers of 10 into metric prefixes; you will have to roll your own functionality for that. But be aware that the period doesn't have to be any of the standard ones; a user could create time measurements based whatever period they like (e.g. years) or even the standard periods but with a different representation type (e.g. double) so you probably shouldn't assume. Why not make it another parameter?

2

What you are trying to do is part of the definition of reflection, more specifically run time type information (RTTI), which gives code the ability to examine at itself at runtime, I took a good look into this a while back when trying to make a system which would serialise all member variables of a passed in type to a format such as { "varName"=<varValue> }

Unfortunately the simple answer is that C++ does not natively support reflection, and where it provides "similar" functionality (i.e. <type_traits> and typeid()) it leaves much to be desired. The best results will come from custom build steps which generate data you require, I decided against this approach so can't really help with the "how to" in this case, but these approaches are obviously limited in portability.

However the approach of template specialisation which you have already mentioned is a step towards the best implementation I've seen yet for C++ runtime reflection, which uses a combination of macros to generate class descriptors.

The MFC macros 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.

To implement something similar you could use something along the lines of;

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

Which would then allow you to access the name through Test::ClassDescriptor::GetName()

There's of course other approaches and your own specifications dictate how you can implement this, for an example of an approach where classes inherit from a templated RTTI class check out the article "Using Templates for Reflection in C++" written by Dominic Filion in the book Game Programming Gems 5

MrShiny608
  • 106
  • 2