0

I have changed my approach from my original question to templatize the entire class instead and place it inside a variadic tuple. I can now use getters and setters the way that I would like them to be created. However, now I am trying to take it a step forward and combine the individual controllers into one controller.

#ifndef CONTROLLER_HPP
#define CONTROLLER_HPP

#include <functional>
#include <vector>
#include <iostream>
#include <utility>

template<typename...Classes>
class Controller
{
  public:
    Controller(Classes&...objects) : objects(objects...){}

    void setValues(int value)
    {
      std::apply([&](auto&...x) { (x.updateValue(value),...);}, objects);
    }

    void getValues(std::vector<int> &values)
    {
      std::apply([&](auto&...x) { (values.push_back(x.get()),...);}, objects);
    }
  private:
    std::tuple<Classes&...> objects;
};

#endif

With this I can do the following:

classA A;
classB B;
classC C;
classD D;
classE E;
classF F;
classG G;

Controller controller1(A,B,C);
Controller controller2(D,E);
Controller controller3(F,G);

controller1.setValues(20);
controller2.setValues(13);
controlelr3.setValues(32);

However, I want to take it a step further and combine the two like so:

Controller master(controller1,controller2,controller3);
master.setValues(40);

I have looked at this post talking about joining variadic templates, however I think this returns a type(?) and not a class. I also tried creating two overloaded classes, however I don't think I am creating the overload correctly:

template<typename...Classes>
class Controller
{
  public:
    Controller(Classes&...objects) : objects(objects...){}

    void setValues(int value)
    {
      std::apply([&](auto&...x) { (x.updateValue(value),...);}, objects);
    }

    void getValues(std::vector<int> &values)
    {
      std::apply([&](auto&...x) { (values.push_back(x.get()),...);}, objects);
    }
  private:
    std::tuple<Classes&...> objects;
};

template<Controller<typename ... > class Controllers, typename ...Classes>
class Controller<Controllers<Classes&...classes>...>
{
  // create a new controller that takes all the combined classes
};

How can I combine any number of templated variadic templated classes into one class? I do have the ability to use C++17.

Sailanarmo
  • 1,139
  • 15
  • 41
  • Make a functor with overloaded `operator()`, one that takes care or `Controller<...>`s and one that take care of anything else. Then you can pass that to `std::apply` instead of your current lambda. – super Apr 30 '19 at 17:28
  • @super would you like to give an example? – Sailanarmo Apr 30 '19 at 17:33
  • @super That would require the child-controllers have a lifetime that exceeds the master for no practical reason. – Yakk - Adam Nevraumont Apr 30 '19 at 17:35

1 Answers1

4
template<typename...Classes>
class Controller
{
  Controller( std::tuple<Classes&...> tup ):objects(tup) {}
public:
  template<class...Rhs>
  Controller<Classes..., Rhs...> operator+( Controller<Rhs...> rhs ) const {
    return std::tuple_cat( objects, rhs.objects );
  }

...

giving us:

Controller master = controller1+controller2+controller3;
master.setValues(40);
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I guess this is one way to do this, however it would require the user to use `+` instead of constructing it inside of the class. – Sailanarmo Apr 30 '19 at 17:33
  • @Sailanarmo Yes, if you feel exposing the algebraic nature of your operation to the consumer is a bad thing. – Yakk - Adam Nevraumont Apr 30 '19 at 17:34
  • Nice approach, very little additional boilerplate. – super Apr 30 '19 at 18:10
  • @Yakk-AdamNevraumont what is wrong with constructing them in parentheses? You said it would require the child-controllers to have a lifetime that exceeds the master? – Sailanarmo Apr 30 '19 at 18:29
  • 1
    @Sailanarmo No: super's implementation (which I could understand from his brief comment) had a problem. I could list things wrong with your plan of passing them via construction; it doesn't reflect the algebraic nature of the operation (the operation distributes, for example), it requires a whole pile of extra boilerplate, and it convolves "control these instances" with "control the instances that these controllers control" (which is the reason why super's solution had the bug I pointed out). This is the turing tar pit, everything is equivalent, but some things are shaped better. – Yakk - Adam Nevraumont Apr 30 '19 at 18:41
  • 1
    @Sailanarmo The fact that your operation is concatination makes it algebraic, and expressing algebraic operations with algebra leads to simple, clear, elegant code. And as your state is a tuple of references, the result is also pretty efficient and easy for a compiler to elide intermediate results. A solution with the syntax you ask for would require at lot more ugly boilerplate. – Yakk - Adam Nevraumont Apr 30 '19 at 18:43
  • @Yakk-AdamNevraumont okay, now I understand this solution a lot better, thank you for clearing that up for me. I will select this as the answer. – Sailanarmo Apr 30 '19 at 18:51