Here are three implementations of the distribution in increasing order of complexity:
First, if we can rely on values ββthat are different from each other, or everything is fine when the repetition values ββare overweight, we can simply index the _values() container:
template<class Enum> struct SimpleEnumDistribution { std::uniform_int_distribution<typename Enum::_integral> dist{0, Enum::_size() - 1}; template<class Generator> Enum operator()(Generator& g) { return Enum::_values()[dist(g)]; } };
Otherwise, we can use the culling selection, pre-calculating the min and max range of the enumeration values:
template<class Enum> struct UniformEnumDistribution { std::uniform_int_distribution<typename Enum::_integral> dist{ *std::min_element(Enum::_values().begin(), Enum::_values().end()), *std::max_element(Enum::_values().begin(), Enum::_values().end())}; template<class Generator> Enum operator()(Generator& g) { for (;;) if (auto value = Enum::_from_integral_nothrow(dist(g))) return *value; } };
If this is ineffective (perhaps the enumeration values ββare sparse), we can calculate the lookup table during initialization:
template<class Enum> struct FastUniformEnumDistribution { std::uniform_int_distribution<std::size_t> dist; std::array<typename Enum::_integral, Enum::_size()> values; FastUniformEnumDistribution() { std::copy(Enum::_values().begin(), Enum::_values().end(), values.data()); std::sort(values.begin(), values.end()); dist.param(std::uniform_int_distribution<std::size_t>::param_type{0u, static_cast<std::size_t>( std::distance(values.begin(), std::unique(values.begin(), values.end())) - 1)}); } template<class Generator> Enum operator()(Generator& g) { return Enum::_from_integral_unchecked(values[dist(g)]); } };
An example .
source share