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

Compile-time counters, revisited

14 October, 201515 October, 2015

(Start at the beginning of the series – and all the source can be found in my github repo) Some time ago I read a blog post showing how to make a compile-time counter: a constexpr function that would return monotonically increasing integers. When I first read it I didn’t…

Read More

Lameness Explained

9 December, 20159 December, 2015

OK, more than one person wanted explanations of The C++ <random> Lame List, so here are some of my thoughts, if only to save people searching elsewhere. Calling rand() is lame because it’s an LCG with horrible randomness properties, and we can do better. And if you’re not calling rand(),…

Read More

More constexpr floating-point computation

13 October, 201515 October, 2015

(Start at the beginning of the series – and all the source can be found in my github repo) In the last post, I covered my first forays into writing C++11-style constexpr floating point math functions. Once I’d done some trig functions, exp, and floor and friends, it seemed like…

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