I am writing tests using the TYPED_TEST
feature of google tests, which allows me to generalize a test to multiple types. I am testing a class template for the types int
and double
. In a test, I would need to generate random numbers. To do so, I have tried using the std::uniform_int_distribution<T>
and the std::uniform_real_distribution<T>
but have ran into static asserts.
As the names indicate, std::uniform_int_distribution<T>
checks if T
is an integral type and std::uniform_real_distribution<T>
checks that T
is a floating point type.
Since my test automatically tests for int
and then for double
, I have been trying to write some kind of function that would allow me to chose the right kind of distribution for the type at compile time. More precisely, something like:
template<class T>
Distribution get_right_distribution(const T& a, const T& b)
{
if(T is integral) // Compile time is needed, runtime
// fails since both if and else have to compile
{
return std::uniform_real_distribution(a, b);
}
else
{
return std::uniform_real_distribution(a, b);
}
}
Note that this is only a pseudocode of what I have been trying to do. This kind of logical branch fails because the if
AND the else
have to compile.
I have done some research on how to do this and I feel like std::is_integral<T>
and std::is_floating_point<T>
are part of the solution, but I have not been able to compile anything so far. I mainly tried two things:
enable_if
. Using the first approach I ended up with an error message telling me my overloads were ambiguous. Using the second approach, I tried some stuff but got lost in the abominable syntax (at least for someone not used to it) which it lead to.
Do you have a suggestion on how this could be accomplished?
PS I would like to see how this could be done, so splitting my test in two would not be an acceptable answer for me.
I you may use C++17, you can make use of if constexpr(...)
:
#include <iostream>
#include <random>
#include <type_traits>
template <typename T>
auto get_right_distribution(const T a, const T b) {
if constexpr(std::is_integral<T>::value) {
return std::uniform_int_distribution(a, b);
}
else {
return std::uniform_real_distribution(a, b);
}
}
int main() {
std::random_device rd;
std::mt19937 gen(rd());
auto int_dis = get_right_distribution(1, 6);
std::cout << int_dis(gen) << "\n";
auto float_dis = get_right_distribution(1.F, 6.F);
std::cout << float_dis(gen) << "\n";
}
For C++11 and C++14, you could use a conditional extra template type parameter in your template parameter list to select the return type as well as the distribution.
C++11:
template <typename T,
typename Distribution = typename std::conditional<
std::is_integral<T>::value,
std::uniform_int_distribution<T>,
std::uniform_real_distribution<T>>::type>
Distribution get_right_distribution(const T a, const T b) {
return Distribution(a, b);
}
C++ 14 (return type deduced by auto
and using the std::conditional_t
helper type short form for std::conditional<...>::type
):
template <typename T,
typename Distribution = typename std::conditional_t<
std::is_integral<T>::value,
std::uniform_int_distribution<T>,
std::uniform_real_distribution<T>>>
auto get_right_distribution(const T a, const T b) {
return Distribution(a, b);
}
I sometimes use std::conditional like this:
template<typename Number>
Number random_number(Number from, Number to)
{
static_assert(std::is_integral<Number>::value
|| std::is_floating_point<Number>::value,
"parameters must be integer or floating point types");
using Distribution = typename std::conditional
<
std::is_integral<Number>::value,
std::uniform_int_distribution<Number>,
std::uniform_real_distribution<Number>
>::type;
// in reality I usually get the generator from another
// function, but for many purposes this is fine.
thread_local static std::mt19937 mt{std::random_device{}()};
thread_local static Distribution dist;
return dist(mt, typename Distribution::param_type{from, to});
}
If you pass the function integer parameters it selects the int
distribution otherwise it selects the real
distribution.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.