(Ab)using abstract classes for ID system for QWidgets

  • Hi,

    for my current and for further Qt projects, I'm developing an ID system for better identifying the origin of signals of my QWidgets and other QObjects.

    The system consists of an ID class that can hold an individual ID (QVariant) as well as a "group ID" (integer).
    The sense of the group ID is that it can be shared and updated automatically. This is realized by a second class "IDList" that holds a QVector<int*>.

    Well, the ID system should manage its IDs by itsself without any further need to ensure appending/inserting/removing IDs. To ensure this, I'd like to have the private sector of those two classes "unaccessable". Well, I could now tell other users only to inherit from those base classes so it is ensured that the private data is safe, but there would still be the possibility to accidentally create a base class object. A second problem is that I'd like to define the ID base class as a friend of the IDList class (a second reason why it is so important not to accidentally directly use one of those classes).

    Well, my idea was now to make those base classes abstract by declaring a non-sense pure virtual function inside it. By doing so, inheritance and total restriction of the private sector is ensured (as far as other users wont willfully cast the object), right?

    Is this good programming style? Or is it maybe completely senseless?

    Thank you for your opinions.

  • Lifetime Qt Champion

    @Binary91-0 said in (Ab)using abstract classes for ID system for QWidgets:

    I'm developing an ID system for better identifying the origin of signals of my QWidgets and other QObjects

    I'm wondering why you need this?

  • @jsulm For example to identify a specific button of a group with a shared slot. I like it better to identify the buttons by a specific ID than by its (sometimes variable) buttonText.
    But beside the need of this, is this coding style good or bad or something between?

  • @Binary91-0 said in (Ab)using abstract classes for ID system for QWidgets:

    @jsulm For example to identify a specific button of a group with a shared slot.

    Although you can do signal/slots this way, or even use QSignalMapper, the easier way these days is to connect your signals to lambdas which pass the necessary identifying number (or even the widget) as parameter of the sender to the slot.

    EDIT @jsulm has shown you the code for just this in his response below.

  • Lifetime Qt Champion

    @Binary91-0 You can use lambdas for that and pass the pointer to the calling widget as parameter to the slot:

    QPushButton *button = ui->button1;
    connect(button, &QPushButton::clicked, [button, this]() { // do something with button});

    There is really no point in implementing heavy ID solution.

  • Moderators

    As others pointed out there's usually no need for and ID system but if you want a simple one you can just use custom QObject properties without any special classes:

    some_button->setProperty("id", 42);
    some_button->setProperty("id_group", "some_group");

    If you really want to you can add some "manager" class to set those or do things like rename group or find its members, but I wouldn't add a special class with special requirements that users need to inherit from. That's a little much for just an id.

    As for friends - remember that "friend is your enemy" :) It hides unexpected dependencies really well and leads to awful problems in complex systems.

  • Lifetime Qt Champion


    In addition to my fellow, there's something already available: the objectName property.

  • Well, at first, thank you really much for all those suggestions! I see, I'm neighter well informed about QObject's already existing functionallity (setProperty, objectName), nor do I know how to easy and well-performend handle Qt's events.

    I heard about lambdas sometimes, but to be honest, I don't know anything about this stuff. I started learning it know, and from one point to another point and from there to another point, I'm walking through function pointers, functors and currently I'm in template systems. Maybe this will help me understanding basic C++ functionallity better.
    I also see know, that my method to connect SIGNALS and SLOTS is obsolete and the new way of doing this is by passing function pointers (again a reason to get used to that stuff I think..). What I (till now) still not know is how the example above from jsulm works, as far as it connects SIGNAL to SLOT by passing 3 instead of normally 4 parameters. But maybe I will get that after I understand how to use lambdas..

  • @Binary91-0
    All of signals/slots/lambdas is covered with little examples in https://doc.qt.io/qt-5/signalsandslots.html. It's not easy reading for a beginner, but is worth working through a couple of times while you get the hang of what's going on.

    • if you use SIGNAL() and SLOT() stile of connection (you shouldn't), you can call sender() in the slot to get a pointer to the object that emitted the signal
    • if you are using the new connection you can just add an argument to the slot that takes a pointer to the sender, for example:
    class Receiver : public QObject{
    explicit Receiver(QObject* parent = nullptr) : QObject(parent) {}
    public slots:
    void doSomething(int value, QObject* sender);

    then you can connect using:

    connect(emitter1, &Emitter::someSignal, myReceiver, std::bind(&Receiver::doSomething,myReceiver,emitter1));
    connect(emitter2, &Emitter::someSignal, myReceiver, std::bind(&Receiver::doSomething,myReceiver,emitter2));
    // etc
    • if you just need this tracing for debug purposes you can just use GammaRay

  • Hi again,

    it took some time for me reading all the stuff about function pointers, functors (and lambdas) as well as virtual functions, templates and the std function type.

    Now I think I did understand the basic about that all and since I understood that lambdas are "converted into functors" or, if no capturing is specified, treated as function pointers, I also understand how they are handled in the QObject::connect system.

    The way I'm doing my connections now is just defining a lambda that calls an individual method with individual captured parameters, mostly a pointer to sender itsself, like this:

    connect(this->pbSomeButton, &QPushButton::clicked, this, [this]() -> void {this->privatePropertyClass->DoSomethingSpecial(this);});

    This way gives me the benefit of calling private Properties of the sender object. By solving this via QObject::sender() inside the slot, I could not call private members of it so I think the lambda solution is pretty nice, I like it! The second benefit is that I am totally free what method I'm calling, I don't need to define specific slot methods with equal signature to the signal methods.

    Thank you all for your help, great job!

Log in to reply