6

I am attempting to send a structure via signals/slots between two threads, my signals/slots are connected properly and I have been able to send QStrings containing parts of my data but now I need to send the whole thing and Structures seem most sensible. However when I try the signal is not sent/recieved. The problem seems to be only with sending/receiving the structure, the processing before and after I have tried many ways.

I cannot use pointers such here or here as my data is generated too fast and memory gets over written or freed (I have tried with pointers and assume references will be similarly effected).

I have already added the Q_DECLARE_METATYPE to my structure. My structure is only a small test for now (to be enlarged later) and is in its own header file.

#ifndef RETURNSTRUCT_H
#define RETURNSTRUCT_H

struct Datastruct
{
    int markeridone;
};

Q_DECLARE_METATYPE(Datastruct);

#endif //RETURNSTRUCT_H

Why might my program be unable to send/receive structures? any help is much appreciated.

I am using windows 7, MinGW 32bit, Qt 5.7.0, Qt Creator 4.0.3

Community
  • 1
  • 1
Harry de winton
  • 969
  • 15
  • 23

4 Answers4

12

Your debug-log should warn you about it - you can only send types known to the meta-system of qt. Using Q_REGISTER_METATYPE you end up registering types associated with the namespace where the definition was made.

Fortunately you can tell Qt about your struct like this:

// after QApplication was instantiated
qRegisterMetaType<Datastruct>("Datastruct");
// but before any class is instantiated that connects signals with this type

And it will not try to infer a namespace by looking at the code. Make sure to re-run qmake (or better yet do a clean), or it might be overlooked when building with QtCreator.

If you later happen to pass template-classes of your types via signals, make sure to register them as well, because even if Qt knows about QList, it doesnt know about QList of your type:

qRegisterMetaType<QList<Datastruct>>("QList<Datastruct>");

On another note: if you #define class aliases, make sure to register them with their real names.

#define std::shared_ptr model_ptr
// you can declare your signals like this:
void my_signal(model_ptr<my_model>);
// but have to register the type like this:
qRegisterMetaType<std::shared_ptr<my_model>>("std::shared_ptr<my_model>");
markus-nm
  • 805
  • 5
  • 8
  • 1
    Why oh why would you ever suggest using `#define` here? Even in C it was unnecessary (you had typedef)! In C++, you're supposed to use type aliases: `using model_ptr = std::shared_ptr`. It's also completely fine to use the alias only, not the underlying type name, as long as you consistently use it everywhere. – Kuba hasn't forgotten Monica Sep 14 '16 at 18:05
  • I was giving an example out of my head that was similar to a case i had, which was a headache to pin down, **in case you are already using #define** - not _suggesting_ to use #define – markus-nm Sep 14 '16 at 18:36
  • Registering a *template* class may not work as is, you may need to `typedef` or `using` it first, and then register the alias. Also, note that double angular braces at the end of a template type are only allowed **C++11 and above**. Don't make this your habit if you work for other projects, too! – iksemyonov Sep 14 '16 at 20:27
  • See: http://stackoverflow.com/questions/26364024/how-to-use-template-types-as-slot-and-signal-parameters-in-multiple-threads and https://steveire.wordpress.com/2011/03/16/implementing-qvariantqmetatype-features-with-template-tricks/ – iksemyonov Sep 14 '16 at 20:30
  • @markus-nm your answer worked excellently, a little follow up if you have time, is there likely be a speed penalty compared to simply sending a QString (or other more basic data)? – Harry de winton Sep 15 '16 at 09:45
  • Is there any documentation that you have to call `qRegisterMetaType` after instantiating `QApplication`? A tested executing the function before entering `main()`. It has worked for me as well. Also note that `qRegisterMetaType()` can be used instead of `qRegisterMetaType("Datastruct")`. – JojOatXGME Jun 25 '17 at 20:13
4

In moment, when You declare structure known to QMetaType using macro Q_DECLARE_METATYPE

struct Datastruct
{
    int markeridone;
};

Q_DECLARE_METATYPE(Datastruct)

you can send this structure via QVariant. Is nice and simply. In Your headers declare:

signals:
    void sendDatastruct(QVariant data);

public slots:
    void getDatastruct(QVariant data);

Using signal in Your code:

.....
Datastruct ds;
.....
QVariant data;
data.setValue(ds);
emit sendDatastruct(data);  // now send signal
.....

Using slot:

void MyObject::getDatastruct(QVariant data)
{
    Datastruct ds = data.value<Datastruct>();
    .....
    // now You can use structure in Your code
}
JaRo
  • 366
  • 4
  • 7
1

I couldn't get this to work without some mods as I got a runtime warning:

startsQObject::connect: No such signal

I read the doc and found that you must only pass const values by reference:

So you end up with:

/* myWorker Class Instance header .h */

struct notifyMsg_t
{
    QString m_TitleSuffix;
    QString m_MessageSuffix;
    int     m_errNumber;
};

Q_DECLARE_METATYPE(notifyMsg_t);

signals:
    void sendDatastruct(const QVariant&);

/* MainWindow Class instance Header .h*/
public slots:
    void getDatastruct(const QVariant& data);

/* MainWindow Implementation .cpp */

connect(myWorker,
        SIGNAL(sendDatastruct(const QVariant&)),
        this,
        SLOT(getDatastruct(const QVariant&))
        );


/* Worker implementation .cpp */

void Worker::notify(QString    TitleSuffix,
                    QString    MessageSuffix,
                    int        errNumber)
{
    notifyMsg_t notifyMsg;
    notifyMsg.m_TitleSuffix = TitleSuffix;
    notifyMsg.m_MessageSuffix = MessageSuffix;
    notifyMsg.m_errNumber = errNumber;


    QVariant data;
    data.setValue(notifyMsg);
    emit wrkrError_dbTaskSig(data);
}

This code worked.

Note that you don't include a dummy variable in the SIGNAL definition i.e.

/* This >> */ (const QVariant&)

/* Not this >> */ const QVariant& data

Don't ask me why.

Best regards

Rob

rgarnett
  • 37
  • 4
0

In case somebody needs a complete working solution (tested on 5.6):

  1. Add file StructSignal.h (or whatever)

    struct StructSignal { QString data; bool checkBox_data; ... }; You don't need to add Q_DECLARE_METATYPE(SearchResult);

  2. In the calling class header file

    public slots: ... void getResult(StructSignal*);

  3. In the calling class cpp file

    connect(&emitClass, SIGNAL(resultParameters(StructSignal*)), this, SLOT(getResult(StructSignal*)));

  4. getResult method

    void callingClass::getResult(StructSignal* ss) {

     qDebug () << "getResult";
    
     qDebug () << "data: " << ss->pattern;
     ...
    

    }

  5. In the method with the emit:

    StructSignal ss; ss.data = ui->data->text(); ss.checkBox_data = ui->checkBox_data->isChecked (); ...

    emit searchParameters(&ss);

  6. You need to include the signal declaration:

    signals: void searchParameters(StructSignal* data);