1

This is mainly to clean up a bunch of code from my constructor. I have around 20+ lines of connect(object, func1, this, func2) in the constructor alone and I am trying to clean up the code by having a std::vector<std::tuple<QObject*,std::function<void()>,std::function<void>>>> connections;

It would work out quite nicely if I could do something like:

std::vector<std::tuple<QObject*,std::function<void()>,std::function<void>>>> connections = {
  std::make_tuple(mySlider, std::bind(&QSlider::sliderReleased,mySlider, std::bind(&Foo::onSliderChanged,this)),
  .
  .
  .
};

And then call it like this:

for(auto &&e : connections)
  connect(std::get<0>(e),std::get<1>(e),this,std::get<2>(e));

However, when I do this I get an error that there is a substitution failure and a std::function<void()> cannot be converted into a function pointer. So decide to change it up and create actual function pointers like the following:

typename void(Foo::*fooFunc)();
typename void(QSlider::*sliderFunc)();

std::vector<std::tuple<QObject*,sliderFunc,fooFunc>> sliderConnections = {
  std::make_tuple(mySlider, &QSlider::sliderReleased, &Foo::onSliderChanged),
  .
  .
  .
};

And same thing, I then try to call it:

for(auto &&e : sliderConnections)
  connect(std::get<0>(e),std::get<1>(e),this,std::get<2>(e));

However this also provides a similar error where there are no conversions. Which doesn't make any sense because now I am actually using a function pointer. Which according to the connection documentation it should be able to take a function pointer to connect them. So either I am passing it in incorrectly. Or what I am trying to achieve is not possible.

Any help would be appreciated!

Sailanarmo
  • 1,139
  • 15
  • 41
  • Please edit your question to provide a [mcve]. Also show any error messages verbatim. For what it's worth you pass a `QObject*` as the first arg to `connect` but the second arg is of type `void(QSlider::*sliderFunc)()` -- a `QObject*` is not implicitly convertible to a `QSlider*`, and the last arg you pass is a pointer to a `Foo` member function which needs to be called against a valid instance of `Foo`. – G.M. Jan 28 '20 at 19:10
  • @G.M. I can edit it to provide a MRE. However, Qt examples are rather difficult to create MRE's because they require at least 3 files in order to reproduce it. And in this case, I would need at least 4. So I can leave instructions on how to reproduce it. – Sailanarmo Jan 28 '20 at 19:18
  • mre or [mcve](https://stackoverflow.com/help/minimal-reproducible-example) means that you need to work a little to reduce the code for your question to a minimum. I think that a single header with a QObject derived class, and a main.cpp source would be enough to build and illustrate your question and fulfill the mre requirements. – Former contributor Jan 29 '20 at 06:28
  • In addition, I don't agree (or I don't understand) your motivations. It seems better in your scenario to use the old string (`char*`) based syntax of `QObject::connect` that is evaluated at run-time. – Former contributor Jan 29 '20 at 07:05
  • @Pedro honestly it is just to make the code look cleaner on the constructor side. Right now I have 20+ lines of code that all say `connect(slider, sliderFunc, this, fooFunc)` for sliders, qSpinboxes, and qdoublespinboxes. So my constructor is rather large. Again, it's just an asthetic look. It looks cleaner to call a function that has a two liner in it to build the connections than it does to slap 20+ connections in a constructor. – Sailanarmo Jan 29 '20 at 07:10
  • But you want to move the mess to somewhere you declare and initialize the vector, which needs to be a c++ source (which will produce nasty error messages when compiled). If you use the old string based syntax you could initialize the vector from a plain text (or XML) external file. The compiler won't check your connections, though. – Former contributor Jan 29 '20 at 07:20
  • @Pedro No, they are private `std::vectors` within my class Foo. You are correct, I moved the mess there. But my source file is the one that builds the connections. It's just preference. And I figured it out thanks to G.M's comment. I intend to write the answer in the morning when I'm not laying in bed. But they were right, my QSlider is not a QObject. As soon as I updated my tuple to take a QSlider* instead of a QObject* it compiled and connected just fine. – Sailanarmo Jan 29 '20 at 07:23

1 Answers1

1

After looking at G.M's comment I realized they were correct. A QObject* is not a QSlider* and therefore when trying to call the function QSlider::sliderReleased it couldn't connect the two because QObject does not have a slider. So once I changed that in the vector of tuples the code compiled just fine.

ex:

typedef void(Foo::*fooFunc)();
typedef void(QSlider::*sliderFunc)();
typedef void(QSpinBox::*spinFunc)();

const std::vector<std::tuple<QSlider*, sliderFunc, fooFunc>> sliderConnections = {
  std::make_tuple(slider1, &QSlider::sliderReleased, &Foo::onSlider1Changed),
  std::make_tuple(slider2, &QSlider::sliderReleased, &Foo::onSlider2Changed),
  std::make_tuple(slider3, &QSlider::sliderReleased, &Foo::onSlider3Changed)
};

const std::vector<std::tuple<QSpinBox*, spinFunc, fooFunc>> spinConnections = {
  std::make_tuple(spin1, &QSpinBox::editingFinished, &Foo::onSpin1Changed),
  std::make_tuple(spin2, &QSpinBox::editingFinished, &Foo::onSpin2Changed),
  std::make_tuple(spin3, &QSpinBox::editingFinished, &Foo::onSpin3Changed)
};

These will be private members in whatever class you are in charge of. And then in the constructor, instead of having 6 lines of connect(object,SIGNAL,object,SLOT), you can then put them into a function and call them like:

for(auto && tup : sliderConnections)
  connect(std::get<0>(tup),std::get<1>(tup),this,std::get<2>(tup));

This successfully connects all the objects to their appropriate functions. Again, it's personal preference. I was just wondering if there was a way and G.M pointed me in the correct direction.

Sailanarmo
  • 1,139
  • 15
  • 41