c++variadic-templatesc++-templates

Is there a way to distribute multiple parameter packs from template class constructor to some functors?


Here is some code that not work:

#include<iostream>

template<typename T1, typename T2, typename T3>
class Composer {
    // pseudocode of constructor
    template<typename... Args_one, typename... Args_two, typename... Args_three>
    explicit Composer(Args_one...args1, Args_two...args2, Args_three...args3) {
        t1_ = T1(args1...);
        t2_ = T2(args2...);
        t3_ = T3(args3...);
    }

    double operator()(int u) {
        auto x1 = t1_(u);
        auto x2 = t2_(float(x1));
        auto x3 = t3_(double(x2));
        return x3;
    }
private:
    T1 t1_;
    T2 t2_;
    T3 t3_;
};

class StageOne {
public:
    StageOne(int a, int b): a_(a), b_(b) {};
    int operator()(int c) const {
        return a_ + b_ + c;
    }
private:
    int a_;
    int b_;
};

class StageTwo {
public:
    StageTwo(float c, float d): c_(c), d_(d) {};
    float operator()(float i) const {
        return c_ + d_ + i;
    }
private:
    float c_;
    float d_;
};

class StageThree {
public:
    StageThree(double x, double y): x_(x), y_(y) {};
    double operator()(double z) const {
        return (x_  + y_) * z;
    }
private:
    double x_;
    double y_;
};

int main()
{
    Composer<StageOne, StageTwo, StageThree> composer{{3, 4}, {1.2f, 2.3f}, {4.5, 3.3}};
    std::cout << composer(3) << std::endl;
}

The Composer template is used to chain three functors. Each functors need to be initializd.

I know I can add methods like setStageOne that utilize a single parameter pack to do the initialization, but I will have to add three such methods and cannot forget to call it afterwards.

So I want the Composer's constructor take the work to set up the functors. Is there a way to do it?


Solution

  • You could put the arguments in std::tuples. Since the types are not default-constructible, you'll need a helper to initialize them from the supplied argument via std::apply (called init below).

    Example:

    #include <tuple>
    #include <utility>
    
    template <class T1, class T2, class T3>
    class Composer {
        template <class T, class... Args>
        T init(std::tuple<Args...>& t) {
            return std::apply(
                [](auto&&... args) {
                    return T{std::forward<decltype(args)>(args)...};
                }, t);
        }
    public:
    
        template <class... Args_one, class... Args_two, class... Args_three>
        explicit Composer(std::tuple<Args_one...> args1,
                          std::tuple<Args_two...> args2,
                          std::tuple<Args_three...> args3)
            : t1_(init<T1>(args1)), t2_(init<T2>(args2)), t3_(init<T3>(args3)) {}
    
        double operator()(int u) {
            auto x1 = t1_(u);
            auto x2 = t2_(float(x1));
            auto x3 = t3_(double(x2));
            return x3;
        }
    
    private:
        T1 t1_;
        T2 t2_;
        T3 t3_;
    };
    

    Demo