Home > Net >  Iterate tuple with "break" or "return" in loop
Iterate tuple with "break" or "return" in loop

Time:09-27

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)) && ...);
    });
}
  • Related