random_device, no strings attached
1. Introduction and motivation
2. Proposed wording for C++20
29.6.6 Class random_device [rand.device]
3. References
[rand.device] /3 defines the constructor of std::random_device as follows [N4659].
This wording apparently has not been touched since the original proposal in 2002 [N1398].
explicit random_device(const string& token = implementation-defined);
Effects: Constructs a
random_devicenondeterministic uniform random bit generator object. The semantics and default value of thetokenparameter are implementation-defined.(Footnote: The parameter is intended to allow an implementation to differentiate between different sources of randomness.)
On libc++, the default and only supported value of this parameter is the 12-character string
"/dev/urandom".
On libstdc++, the supported values include "/dev/urandom", "/dev/random",
"default", "mt19937", and anything that looks like an input to strtoul;
the default value changes depending on user-controllable preprocessor macros.
Notice that there is no way to construct an object of type std::random_device
without first constructing an object of type std::string. This may be problematic
for codebases where using the heap is forbidden.
Furthermore, std::random_device's constructor is not allocator-aware;
it requires that the input string be constructed using std::allocator.
This requires linking in (and template-instantiating) all of the machinery of
std::allocator, including operator new, even in programs that
just want the default behavior of std::random_device. This machinery will be
instantiated, compiled, and linked in, even if the implementation-defined default value
is subject to Small String Optimization!
There are many possible overload sets which would adequately address my concern here:
() and (const string&) — my proposed solution() and (string_view)(string_view = default)(const string&) and (string_view = default)() and (const string&) and (string_view)string_view. Solutions 4 and 5 introduce
the possibility of overload resolution ambiguity in the case of random_device(X())
where X() is implicitly convertible to either string or string_view.
Any solution involving string_view has the pitfall that a user-provided
string_view is not necessarily null-terminated; so if the constructor wants to
pass it to fopen (for example), it will have to copy the user-provided bytes into
a null-terminated array (perhaps allocated, perhaps of size PATH_MAX/MAX_PATH
on the stack). It is better to use the user-provided buffer directly.
The wording in this section is relative to WG21 draft N4659 [N4659], that is, the current draft of the C++17 standard.
Edit paragraphs 2 and 3 as follows.
class random_device {
public:
// types
using result_type = unsigned int;
// generator characteristics
static constexpr result_type min() { return numeric_limits<result_type>::min(); }
static constexpr result_type max() { return numeric_limits<result_type>::max(); }
// constructors
random_device();
explicit random_device(const string& token= implementation-defined);
// generating functions
result_type operator()();
// property functions
double entropy() const noexcept;
// no copy functions
random_device(const random_device&) = delete;
void operator=(const random_device&) = delete;
};
random_device();
explicit random_device(const string& token= implementation-defined);
Effects: Constructs a
random_devicenondeterministic uniform random bit generator object. The semanticsand default valueof thetokenparameter are implementation-defined.(Footnote: The parameter is intended to allow an implementation to differentiate between different sources of randomness.)