Search code examples
c++boost-signalspublic-fields

Public boost::signal object


I make my boost::signals public because I'm lazy.

class Button {
public:
    signal<void()> clicked;
};

int main() {
    Button btn;
    btn.clicked.connect(handleClick);
}

... rather than encapsulating with a Button::OnClicked(boost::function<void()>).

Is this going to come back and bite me?


Solution

  • It depends.

    It has bitten me before when I wanted to add some special logic each time an object connected to another object's signals. This is the most likely case to bite you.

    Also, it can make it difficult to keep track of exactly when other objects are connecting to any given object.

    I would say hide the connections behind a function to be on the safe side.

    I usually use a macro to do the vanilla function definition.

    #define SIGNAL(slot,name) connection name(function<slot> func) { return _##name##.connect(func);}
    

    And then in a class definition:

    SIGNAL(void(),clicked)
    

    This assumes you follow the convention of naming the signal '_clicked' but you can substitute any convention. It generally keeps the interface cleaner for all of your classes. When you want to add special connection logic you can, without changing all of the other objects that use the signal.

    EDIT

    One instance was when the signal object was actually moved to a delegate implementation inside another class, but it still made sense for objects to connect through the original class. This broke all of the places that tried to connect to it. If they had been using function accessors to connect, it would have been as simple as changing the function to look up the signal in the delegate. But as it was it broke all the users of the original class.

    Or, when I wanted to log each time something connected to a specific signal. This was just for debugging purposes, but it can be very helpful if you suspect something wonky is going on like cycles in your signal connections.