I have a tuple of Args
generated from a function signature.
I want to iterate the tuple and check if the argument is a specific type, and return a string if it isn't.
So I came up with the following code:
std::tuple<Args...> arguments;
Object* runtime_arguments = new Object[N];
fill_args(runtime_arguments, N);
auto is_valid_arguments = std::apply([&runtime_arguments](auto... arguments) {
std::size_t i = 0;
for (auto arg : {arguments...}) // Works only if `args` is homogenous.
{
std::string error;
if (!is_valid_type<decltype(arg)>(runtime_arguments[i ], error))
{
return false;
}
}
return true;
}, arguments);
template<typename T>
bool is_valid_type(Object* object, std::string& error)
{
if constexpr(std::is_same<T, std::int32_t>::value)
{
return object->is_type('I');
}
else
{
static_assert(!is_native_type<T>::value, "Invalid Type");
}
return false;
}
I can do it at compile time, but then it'd return a tuple of booleans and error strings and I don't want that. I want it to break the loop and return immediately when there is an issue as shown above.
This works fine if std::initializer_list<auto>{arguments...}
is homogenous, so if the tuple is: std::tuple<Element*, char, bool>
it won't compile of course.
Is there a way to loop over the elements in the tuple, and break on first issue? Maybe something like:
for_each(tuple, [](auto& e) {
return true; // continues looping?
return false; //stops the loop
});
So I thought of something like:
template<size_t I = 0, typename Fn, typename... Tp>
void for_each(std::tuple<Tp...>&& t, Fn &&fn)
{
if (!fn(std::get<I>(t)) {
return false;
}
if constexpr(I 1 != sizeof...(Tp)) {
return for_each<I 1>(std::forward<std::tuple<Tp...>>(t), std::forward<Fn>(fn));
}
}
Is there a better way to do this or similar (preferably without recursion)?
CodePudding user response:
Fold over a short circuiting operator (In this case, &&
):
bool is_valid_arguments = std::apply([&runtime_arguments](auto... arguments) {
std::size_t i = 0;
return ([&]{
std::string error;
return is_valid_type<decltype(arguments)>(runtime_arguments[i ], error);
}() && ...);
}, arguments);
Your requested for_each
function can be written like this:
template<typename Tuple, typename F>
void for_each(Tuple&& tuple, F&& f) {
return std::apply(std::forward<Tuple>(tuple), [&f](auto&&... args) {
return (f(std::forward<decltype(args)>(args)) && ...);
});
}