Skip to content
Why is a raven like a writing desk?

Thoughts both confusing and enlightening.

Why is a raven like a writing desk?

Thoughts both confusing and enlightening.

CHRONO + RANDOM = ?

elbeno, 24 October, 201624 October, 2016

Being a quick sketch combining <chrono> and <random> functionality, with cryptarithmetic interludes…

At CppCon this year there were several good talks about randomness and time calculations in C++. On randomness: Walter Brown’s What C++ Programmers Need to Know About Header <random> and Cheinan Marks’ I Just Wanted a Random Integer! were both excellent talks. And Howard Hinnant gave several great talks: A <chrono> Tutorial, and Welcome to the Time Zone, a followup to his talk from last year, A C++ Approach to Dates and Times.

CHRONO + RANDOM = HORRID ?

That’s perhaps a little unfair, but recently I ran into the need to compute a random period of time. I think this is a common use case for things like backoff schemes for network retransmission. And it seemed to me that the interaction of <chrono> and <random> was not quite as good as it could be:

system_clock::duration minTime = 0s;
system_clock::duration maxTime = 5s;
uniform_int_distribution<> d(minTime.count(), maxTime.count());
// 'gen' here is a Mersenne twister engine
auto nextTransmissionWindow = system_clock::duration(d(gen));

This code gets more complex when you start computing an exponential backoff. Relatively straightforward, but clumsy, especially if you want a floating-point base for your exponent calculation: system_clock::duration has an integral representation, so in all likelihood you end up having to cast multiple times, using either static_cast or duration_cast. That’s a bit messy.

I remembered some code from another talk: Andy Bond’s AAAARGH!? Adopting Almost Always Auto Reinforces Good Habits!? in which he presented a function to make a uniform distribution by inferring its argument type, useful in generic code. Something like the following:

template ,
          typename D = std::uniform_int_distribution>
inline auto make_uniform_distribution(const A& a,
                                      const B& b = std::numeric_limits::max())
  -> std::enable_if_t::value, D>
{
  return D(a, b);
}

Of course, the standard also provides uniform_real_distribution, so we can provide another template and overload the function for real numbers:

template ,
          typename D = std::uniform_real_distribution>
inline auto make_uniform_distribution(const A& a,
                                      const B& b = B{1})
  -> std::enable_if_t::value, D>
{
  return D(a, b);
}

And with these two in hand, it’s easy to write a uniform_duration_distribution that uses the correct distribution for its underlying representation (using a home-made type trait to constrain it to duration types).

template 
struct is_duration : std::false_type {};
template 
struct is_duration> : std::true_type {};

template ::value>>
class uniform_duration_distribution
{
public:
  using result_type = Duration;

  explicit uniform_duration_distribution(
      const Duration& a = Duration::zero(),
      const Duration& b = Duration::max())
    : m_a(a), m_b(b)
  {}

  void reset() {}

  template 
  result_type operator()(Generator& g)
  {
    auto d = make_uniform_distribution(m_a.count(), m_b.count());
    return result_type(d(g));
  }

  result_type a() const { return m_a; }
  result_type b() const { return m_b; }
  result_type min() const { return m_a; }
  result_type max() const { return m_b; }

private:
  result_type m_a;
  result_type m_b;
};

Having written this, we can once again overload make_uniform_distribution to provide for duration types:

template ,
          typename D = uniform_duration_distribution>
inline auto make_uniform_distribution(const A& a,
                                      const B& b = B::max()) -> D
{
  return D(a, b);
}

And now we can compute a random duration more expressively and tersely, and, I think, in the spirit of the existing functionality that exists in <chrono> for manipulating durations.

auto d = make_uniform_distribution(0s, 5000ms);
auto nextTransmissionWindow = d(gen);

CHRONO + RANDOM = DREAMY

I leave it as an exercise for the reader to solve these cryptarithmetic puzzles. As for the casting problems, for now, I’m living with them.

C++

Post navigation

Previous post
Next post

Related Posts

The Global API Injection Pattern

12 April, 202614 April, 2026

I’ve mentioned this in a few conference talks now, and periodically people ask me about it, but until now I guess I haven’t written it up. It starts with a common, dare I say ubiquitous, problem: how to test code that uses some “global” API. Could be for example file…

Read More

Development of an Algorithm

21 June, 201722 June, 2017

Here’s an exercise: given a nice piece of code sitting in a file, how do you take that code and make it generic, in the style of an STL algorithm? For our example, let’s consider an algorithm that isn’t (yet) in the STL. First, the problem it solves. Imagine that…

Read More

How to print anything in C++ (part 4)

2 February, 201530 June, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript Callable things. There are several types: functions member functions std::functions bind expressions lambdas objects that support operator() (function objects) So, going back to my tag code, so far (with everything I’ve added) and including callable things, it will look…

Read More

Comment

  1. Ilya says:
    22 December, 2016 at 4:42 am

    check out my project: https://github.com/effolkronium/TinyRandom

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

©2026 Why is a raven like a writing desk? | WordPress Theme by SuperbThemes