Search code examples
c++signals-slotspimpl-idiom

Opaque Pointer (pimpl) and signals and slots


I am getting more and more into the Pimpl idiom (private opaque pointer to real class implementation). But I still have an issue which bothers me.

How does this idiom\design pattern deal with signals in the public class (like boost or qt signals)?

class my_class : public QObject 
{
Q_OBJECT
public:
   void monitorstuff();
signal:
   void needupdate();
private:
    class impl; unique_ptr<impl> pimpl; // opaque type here
};


class my_class::impl {  
    void reallymonitorstuff();
};

my_class::impl::reallymonitorstuff()
{
  ...
  //update required here
  ...
}

void my_class::monitorstuff()
{
  pimpl->reallymonitorstuff();
}
  • Do I replicate all signals in the pimpl, connect with signals of the outer class? A bit annoying to have twice as much signals as what is publicly available, also annoying when I need to swap instances.
  • Do I pass the public instance as parameter to the private instance which calls directly the public signals
  • Another design mechanism in conjuction I didn't heard of?

Solution

  • In general, I don't really see the problem. The public class should forward all calls to the impl including calls to connect a slot. The impl contains the signal, not the public class. E.g here using Boost.Signals2:

    #include <memory>
    #include <boost/signals2.hpp>
    #include <iostream>
    
    using signal_type = boost::signals2::signal<void()>;
    using slot_type = signal_type::slot_type;
    
    class my_class {
    public:
       my_class(); 
       void monitorstuff();
       void connect(const slot_type& slot);
    private:
       struct impl; std::unique_ptr<impl> pimpl;
    };
    
    struct my_class::impl {
      signal_type signal;
      void reallymonitorstuff();
      void connect(const slot_type& slot){ signal.connect(slot); }
    };
    
    void
    my_class::impl::reallymonitorstuff() {
      //...
      signal();
      //...
    }
    
    void my_class::monitorstuff() {
      pimpl->reallymonitorstuff();
    }
    
    void my_class::connect(const slot_type& slot) {
      pimpl->connect(slot);
    }
    
    my_class::my_class() : pimpl(std::make_unique<my_class::impl>()){}
    
    int main() {
        my_class mc;
        auto slot = []{ std::cout << "Notified!\n"; };
        mc.connect(slot);
        mc.monitorstuff();
    }
    

    Live demo.

    I wonder if your problem is more specific to Qt.