Как хранить функции variadi c внутри вектора, который будет вызываться при построении класса? - PullRequest
0 голосов
/ 02 апреля 2020

Я не уверен, что то, что я спрашиваю, возможно, так как я думаю, что смешиваю время компиляции и время выполнения.

Скажем, у меня есть класс ExClass. И этот класс имеет 10 строк виджетов для добавления на панель. Перед реализацией шаблона я бы сделал:

void ExClass::addRow0()
{
  this->addWidget(widget1,row,col); //where row and col are different numbers, ex 0,0
  this->addWidget(widget2,row,col); // 0,1
  this->addWidget(widget3,row,col); // 0,2
  this->addWidget(widget4,row,col); // etc.
}

void ExClass::addRow ... 10(){ ... }

Затем конструктор класса выглядел бы так:

ExClass::ExClass()
{
  addRow0();
  addRow1();
  .
  .
  .
  addRow10();
}

Затем я решил сделать функции variadi c как частные функции в ExClass, которые будут принимать любое количество виджетов и автоматически строить строки следующим образом:

const int MAXCOLSIZE = 2;

template<typename T>
void addToWidget(T* widget,int row,int size)
{
  int col = std::abs(size - MAXCOLSIZE);
  this->addWidget(widget,row,col,Qt::AlignHCenter);
}

template<typename T>
void addRow(int row, T* widget)
{
  this->addWidget(widget,row,MAXCOLSIZE,Qt::AlignHCenter);
}

template<typename Widget, typename... T>
void addRow(int row, Widget *w,T*...t)
{
  constexpr std::size_t n = sizeof...(T);
  addToWidget(w,row,n);
  addRow(row,t...);
}

template<typename T>
void addSingle(int row, T *widget)
{
  this->addWidget(widget,row,0,1,MAXCOLSIZE+1,Qt::AlignVCenter);
}

Причина, по которой я сделал это таким образом, заключается в том, что мне не пришлось бы создавать 10 функций addRowN для вызова в конструкторе. Тогда это сэкономит мне немного кода и хлопот. Тем не менее, это сделало мой конструктор похожим на это:

ExClass::ExClass()
{
  addRow(0,widget1,widget2,widget3);
  addRow(1,widget4,widget5,widget6);
  addSingle(2,widget7);
  addRow(3,widget8,widget9,widget10);
  .
  .
  .
  addRow(10,widget23,widget24,widget25);
}

Который, думая об этом сейчас, гораздо менее читабелен и в некоторых местах может сойти с ума. Поэтому я могу вернуться к исходному способу и вызвать его через специальный частный вектор, который я создал:

std::vector<std::function<void()>> rowFuncs = {
  std::bind(&ExClass::addRow0,this),
  . 
  .
  .
  std::bind(&ExClass::addRow10,this)
};

Однако, прежде чем я go вернусь к этому, мне было интересно, есть ли способ, которым я может передать мою функцию variadi c в вектор таким же образом? Я попробовал:

std::vector<std::function<void(...)>> rowFuncs;

Однако мне не нравится, что он не знает, какого типа параметры, и я не могу их сохранить. Поэтому мне интересно, возможно ли то, что я пытаюсь сделать? Было бы замечательно, если бы я мог разбить его на функцию, чтобы я мог очистить свой конструктор, чтобы это было не 10+ строк кода, а функция, которая вызывает вектор функций, которые будут вызываться, как я делал с другой реализацией:

void ExClass::buildLayout()
{
  for(const auto& fn : rowFuncs)
    fn();
}

тогда мой конструктор будет радостно выглядеть так:

ExClass::ExClass()
{
  buildLayout();
}

У меня buildLayout работает в исходной реализации функции. Однако я хотел бы знать, возможно ли это сделать с моей реализацией функции variadi c. Если у кого-то есть какие-либо предложения или если это вообще возможно, я был бы признателен за это!

РЕДАКТИРОВАТЬ: я должен отметить, что ExClass в этом случае имеет 25 уникальных виджетов. Все частные члены класса.

1 Ответ

0 голосов
/ 03 апреля 2020

Следуя совету от комментария @ Midren. Я смог добиться того, чего хотел. Однако мне нужно было также добавить определение шаблона. Это был рабочий пример:

class ExClass
{
  public:
    ExClass();

    void constructLayout();

  private:

    const int MAXCOLSIZE = 2;

    template<typename T>
    void addToWidget(T* widget,int row,int size)
    {
      int col = std::abs(size - MAXCOLSIZE);
      this->addWidget(widget,row,col,Qt::AlignHCenter);
    }

    template<typename T>
    void addRow(int row, T* widget)
    {
      this->addWidget(widget,row,MAXCOLSIZE,Qt::AlignHCenter);
    }

    template<typename Widget, typename... T>
    void addRow(int row, Widget *w,T*...t)
    {
      constexpr std::size_t n = sizeof...(T);
      addToWidget(w,row,n);
      addRow(row,t...);
    }

    template<typename T>
    void addSingle(int row, T *widget)
    {
      this->addWidget(widget,row,0,1,MAXCOLSIZE+1,Qt::AlignVCenter);
    }

    Widget *widget1 = new Widget();
    Widget *widget2 = new Widget();
    Widget *widget3 = new Widget();
    Widget *widget4 = new Widget();

    std::vector<std::function<void()>> rowFuncs = {
      std::bind(&ExClass::addRow<Widget,Widget>,this,0,widget1,widget2),
      std::bind(&ExClass::addSingle<Widget>,this,1,widget3),
      std::bind(&ExClass::addSingle<Widget>,this,1,widget4)
    };
};

Затем вы можете построить макет следующим образом:

void ExClass::constructLayout()
{
  for(const auto& fn : rowFuncs)
    fn();
}
...