Better implementation of signals use void * or not



  • This is probably more of a generalized best practices question involved how to generalize parameters in a signal/slot for QT. I am like to hear any tips on how to best implement a parametrized signal for a class that I want to output a variety of different types.

    I have a processing unit that is used to process an incoming stream from the network and it uses a signal to notify a slot that relevant information has been parsed from a data packet and attaches the relevant information to a void * in the signal.

    For example I have a signal on the processing class called:
    @
    void EventPacketParsed( int packetFrameId, int protocolType, int packetTypeId, void * object );
    @
    The first three parameters are unique to our communication protocol and the last parameter void * object will contain the parsed data packet information. Lets say that this communication protocol had the following data that it transmits, gps information, device information, and other information.

    so we had a
    @
    struct gpsInfo
    {
    ... information ...
    }

    struct deviceInfo
    {
    ... information ...
    }

    struct otherDataTypes...
    {
    ... information ...
    }
    @
    So when the Processing class processes gpsData it will call the EventPacketParsed
    @
    HandleParsedData()
    {
    On GPS Packet:
    Extract GPS Data to struct GPS
    EventPacketParsed(packetFrameId, protocolType, packetTypeId, (void *) gpsData);

    On Device Info Packet:
    Extract Device Info Data to struct deviceInfo
    EventPacketParsed(packetFrameId, protocolType, packetTypeId, (void *) deviceInfo);

    ... On Other Info Possibilities:
    Extract Data to corresponding structure or class
    EventPacketParsed(packetFrameId, protocolType, packetTypeId, (void *) corrispondingInfo);

    }
    @
    Then the class that is receiving slot for the event will know what data types is transferred for each corresponding packetTypeId
    @
    OnPacketParsedSlot( int packetFrameId, int protocolType, int packetTypeId, void * object )
    {
    When packetTypeId == PACKET_TYPE_GPS_DATA
    cast void * object to struct gpsInfo
    use data however the class needs to

    When packetTypeId == PACKET_TYPE_DEVICE_INFO_DATA
    cast void * object to struct deviceInfo
    use data however the class needs to

    ... have a case for all relevant data types ...

    }
    @
    So the whole reasoning behind this setup is that class with the slot does not have to worry about how the data is coming in to be parsed, it just cares about what the packetTypeId is and knowing how to cast the void * object based on the packetTypeId.

    So we could have just a binary packet, JSON stream, XML format and each one will get converted to the project struct gpsInfo, struct deviceInfo, etc... and call its EventPacketParsed and each corresponding slot will be able to handle that call the exact same.

    So is using void * for this type of event where different data types could be packed into the signal, which is unknown at compile time, or is there a better way to implement a generalized signal object parameter.



  • It is an option, and it will work. But IMHO, it is not very OO-like design. I think it would be nicer to do something like:

    @
    class Info {
    public:
    enum Type {
    GPS,
    Device,
    Other
    }

    virtual Type type() const = 0;
    virtual ~Info() {};
    };

    class Gps: public Info
    {
    public:
    virtual Type type() const {return Gps;}
    //Gps specific methods
    }

    //same for Device and Other and whatever else you need
    @
    Then, your signal doesn't need the type parameter anymore. It only needs an Info* parameter. That class can then be either queries using the type() method, or just be dynamic_cast to the right class, or used with whatever other nice OO tricks you can do :-)


  • Moderators

    You can also use QVariant to pass values, although it seems that in your use case a custom class is a better choice.



  • Since you talk about 'best practice', I'm going to give my opinion and how I've been solving simular situations. Take it for what its worth :)

    First it looks a little bit like a round peg in square hole to fit 3 message types into one slot.
    Why not have 3 slots?
    They could share code by calling some private methods with specific data that all those message types share.

    You would then have;
    @
    class Foo : public QObject {
    Q_OBJECT
    public:
    Foo(QObject *parent=0);
    signals:
    incomingEvent(const GpsInfo &info);
    incomingEvent(const DeviceInfo &info);
    incomingEvent(const OtherInfo &info);
    public slots:
    receivedGpsInfo(const GpsInfo &info);
    receivedDeviceINfo(const DeviceInfo &info);
    // etc.
    };
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.