Phil-ZXX
Phil-ZXX

Reputation: 3255

Parameter pack expansion for variadic class member (tuple or other)

I am trying to store a tuple of references in a class (via a variadic template), and then I want to "loop" over them and assign them values.

Function process2 below works as expected, but I want to make function process1 work the same (making use of the stored references of the class itself). However, I can't make process1 compile. What's the correct way? Is there a way to maybe have member Args&... args; instead of std::tuple<Args&...> args (as that might allow the parameter expansion)? Any suggestions appreciated.

Sample Code:

#include <tuple>
#include <string>
#include <iostream>

template<class... Args>
class Handler
{
private:
    template <class T>
    static bool process_arg(T& val)
    {
        if constexpr (std::is_same_v<T, int>)
            val = 123;
        else if constexpr (std::is_same_v<T, std::string>)
            val = "string";
        else
        {
            // do something
            return false;
        }
        return true;
    }

public:
    const std::tuple<Args&...> args;

    Handler(Args&... args)
        : args(args ...) { }

    // bool process1() const
    // {
    //     // Compile Error: operand of fold expression has no unexpanded parameter packs
    //     const bool success = (process_arg(args) && ...);

    //     // Compile Error: no matching function for process_arg(int&, int&, std::string&)
    //     bool success = true;
    //     std::apply([&success](auto &&... v) { success = success && process_arg(v...); }, args);
        
    //     return success;
    // }

    template<class... Args2>
    static bool process2(Args2&... args2)
    {
        const bool success = (process_arg(args2) && ...);
        return success;
    }
};

int main()
{
    int a, b;
    std::string c;

    // Handler(a, b, c).process1();
    Handler<>::process2(a, b, c);

    std::cout << a << "," << b << "," << c << "\n";

    return 0;
}

Upvotes: 1

Views: 424

Answers (2)

Pepijn Kramer
Pepijn Kramer

Reputation: 12848

Not sure this is what you need, since it doesn't store anything as members. But I think it gives the desired output. O well... maybe there is something for you to take away from this :)

class Handler
{
private:
    template <class T>
    static bool process_arg(T& val)
    {
        if constexpr (std::is_same_v<T, int>)
            val = 123;
        else if constexpr (std::is_same_v<T, std::string>)
            val = "string";
        else
        {
            // do something
            return false;
        }
        return true;
    }

public:
    template<typename arg_t, typename... args_t>
    static constexpr bool process(arg_t& arg, args_t&... args)
    {
        if constexpr (sizeof...(args_t) > 0)
        {
            bool success = process_arg<arg_t>(arg) && process(args...);
            return success;
        }

        return process_arg<arg_t>(arg);
    }
};

int main()
{
    int a, b;
    std::string c;
    Handler::process(a, b, c);
    std::cout << a << "," << b << "," << c << "\n";

    return 0;
}

Upvotes: 1

cigien
cigien

Reputation: 60218

You're on the right track with std::apply but the syntax is incorrect; the pack expansion needs to be outside the call to process_arg. Also, you don't need the variable success at all; you can use a fold-expression directly:

bool process1() const
{
  return std::apply([](auto &&... v) {
    return (process_arg(v) && ...); 
  }, args); 
}

Here's a demo

Upvotes: 3

Related Questions