I've got a parameter pack saved as a tuple in some function traits struct. How can I find out, how many of those parameters are std::optional types?
I tried to write a function to check each argument with a fold expression, but this doesn't work as I only pass a single template type which is the tuple itself.
void foo1(){}
void foo2(int,float){}
void foo3(int, std::optional<int>, float, std::optional<int>){}
void foo4(int, std::optional<int>, bool){}
template<typename R, typename... TArgs>
struct ftraits<R(TArgs...)>
{
using ret = R;
using args = std::tuple<TArgs...>;
};
template<typename T>
struct is_optional : std::false_type
{
};
template<typename T>
struct is_optional<std::optional<T>> : std::true_type
{
};
template<typename... Ts>
constexpr auto optional_count() -> std::size_t
{
// doesn't work since Ts is a single parameter with std::tuple<...>
return (0 ... (is_optional<Ts>::value ? 1 : 0));
}
int main() {
using t1 = typename ftraits<decltype(foo1)>::args;
std::cout << optional_count<t1>() << std::endl; // should print 0
using t2 = typename ftraits<decltype(foo2)>::args;
std::cout << optional_count<t2>() << std::endl; // should print 0
using t3 = typename ftraits<decltype(foo3)>::args;
std::cout << optional_count<t3>() << std::endl; // should print 2
using t4 = typename ftraits<decltype(foo4)>::args;
std::cout << optional_count<t4>() << std::endl; // should print 1
}
CodePudding user response:
You can use template partial specialization to get element types of the tuple
and reuse the fold-expression
template<typename>
struct optional_count_impl;
template<typename... Ts>
struct optional_count_impl<std::tuple<Ts...>> {
constexpr static std::size_t count =
(0 ... (is_optional<Ts>::value ? 1 : 0));
};
template<typename Tuple>
constexpr auto optional_count() -> std::size_t {
return optional_count_impl<Tuple>::count;
}
CodePudding user response:
You can get the size of the tuple using std::tuple_size
, then iterate over all its members using a recursive template. In that template, you can pretend to construct an instance of the tuple using std::declval
, get the value at the current index using std::get
, and then finally get the type of that value using decltype
.
Example implementation:
#include <optional>
#include <tuple>
#include <utility>
template<typename T>
struct is_optional : std::false_type {};
template<typename T>
struct is_optional<std::optional<T>> : std::true_type {};
template<typename Tuple, size_t i>
constexpr size_t optional_count_impl() {
size_t val = is_optional<std::remove_reference_t<decltype(std::get<i>(std::declval<Tuple>()))>>::value ? 1 : 0;
if constexpr (i) {
val = optional_count_impl<Tuple, i - 1>();
}
return val;
}
template<typename Tuple>
constexpr size_t optional_count() {
const size_t tuple_size = std::tuple_size<Tuple>::value;
if constexpr (tuple_size == 0) {
return 0;
} else {
return optional_count_impl<Tuple, tuple_size - 1>();
}
}
using Tup1 = std::tuple<int, int, std::optional<size_t>, bool, std::optional<bool>, std::optional<std::optional<int>>>;
int main() {
return optional_count<Tup1>();
}