Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Sharing a header file between C and C++, and using Qt-specific macros via #ifdef

Sharing a header file between C and C++, and using Qt-specific macros via #ifdef

Scheduled Pinned Locked Moved Solved QML and Qt Quick
qmlc++ to c
4 Posts 2 Posters 139 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    AnttiK
    wrote last edited by
    #1

    Hi all,

    For a few days now I've been struggling to understand what exactly is going wrong in my setup. I have a header file that contains code that looks like the following, in two projects which use CMake:

    #if defined(Q_MOC_RUN)
    #include <QtQml/qqmlregistration.h>
    #endif /* Q_MOC_RUN */
    
    #include <stdbool.h>
    
    typedef struct _GetIntStatusResponse_t
    {
    #if defined(Q_MOC_RUN)
        Q_GADGET
    
        Q_PROPERTY(bool clearToSend MEMBER getClearToSend)
        Q_PROPERTY(bool error MEMBER getError)
        Q_PROPERTY(bool rsqInterrupt MEMBER getRsqInterrupt)
        Q_PROPERTY(bool rdsInterrupt MEMBER getRdsInterrupt)
        Q_PROPERTY(bool seekTuneCompletedInterrupt MEMBER getSeekTuneCompletedInterrupt)
    #endif /* Q_MOC_RUN */
    
        /* When set, the device is ready to receive the next command */
        bool clearToSend;
    
        /* When set, the device has encountered an error */
        bool error;
    
        /* When set, a Received Signal Quality interrupt has been triggered */
        bool rsqInterrupt;
    
        /* When set, an Radio Data System interrupt has been triggered */
        bool rdsInterrupt;
    
        /* When set, the Seek/Tune Complete interrupt has been triggered */
        bool seekTuneCompletedInterrupt;
    } GetIntStatusResponse_t;
    

    The intent here is that when the C program compiles the header, it only sees the boolean members but when a Qt application does, it also invokes the meta-object complier to generate whatever it needs to in order for the properties to be accessible from a QML element.

    The header itself is ferried over as a CMake configuration file together with a target file, and target_link_libraries is used to include it to the Qt application. This is enough, and commands like #include "header.h" work without a hitch.

    Using a debugger I have verified that the structs arrive correctly to the C++ application side: they are read from a byte array sent over USB, and when std::memcpy is used the debugger shows that the content of the structure instance looks correct compared to what Wireshark is showing on the wire. From this I deduce that the data moves correctly between the C program, USB and the C++ application.

    However, when an instance of the struct is emitted through a signal, and arrives to the QML side it is completely empty i.e. using console.log together with JSON.stringify I get the following the application's console:

    qml: Received status report: QVariant(_GetIntStatusResponse_t, )
    qml: Stringified: ""
    

    Naturally, if I try to access any of the members I get undefined. The data itself has mysteriously vanished somewhere during the conversion from a C++ instance to a QVariant-wrapped object.

    I can provide more code and details if necessary but does anyone have any ideas on what could be going wrong? I have tried stepping through the code that comes when I call emit but it goes so deep into how Qt works that I just can't make sense of it.

    I have already tried the following:

    • Using #ifdef __cplusplus instead; this leads to a number of linker errors related to a function called staticMetaObject, one per struct type
    • Omitting Qt stuff from the structs in the shared header, and instead defining more struct in the C++ side; this works but seems rather hacky. There must be a way to coax MOC to do my bidding.
    • Including the shared headers inside qt_add_executable; this has no effect, the properties are still undefined.
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote last edited by
      #2

      Hi,

      Do I understand correctly that you are trying to load a binary representation of one struct in C into what is technically a different struct in C++ ?

      memcpy is the wrong tool here, you should initialize your C++ struct based on the C one. These two have different memory layouts thus overwriting the memory taken by the latter with the former is a recipe for tragedy.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      A 1 Reply Last reply
      0
      • SGaistS SGaist

        Hi,

        Do I understand correctly that you are trying to load a binary representation of one struct in C into what is technically a different struct in C++ ?

        memcpy is the wrong tool here, you should initialize your C++ struct based on the C one. These two have different memory layouts thus overwriting the memory taken by the latter with the former is a recipe for tragedy.

        A Offline
        A Offline
        AnttiK
        wrote last edited by AnttiK
        #3

        @SGaist What I'm doing is creating an instance of a "report struct" that uses a union type in the C application, filling the proper portion of that, send it as a HID report, and then parse the bytes on the other side.

        Here is the glue which makes it work:

        typedef struct _Report_t
        {
            /* Identifier of the report */
            ReportIdentifier_t identifier;
        
            /* Report bytes */
            union ReportBytes {
                GetIntStatusResponse_t interruptStatus;
                ... other structs ...
        
                //* This defines the "maximum size" the report can have; one byte is reserved for the identifier */
                uint8_t raw[MAX_REPORT_SIZE - 1];
            } bytes;
        } Report_t;
        

        The relevant portion of the C++ code looks like this. It uses the hidapi library:

        unsigned char buf[MAX_REPORT_SIZE];
        
        int res = hid_read_timeout(m_source, buf, sizeof(buf), 250);
        if (res > 0)
        {
            ReportIdentifier_t identifier = (ReportIdentifier_t)buf[0];
        
            switch (identifier)
            {
            ... other report types ...
        
            case REPORT_IDENTIFIER_INTERRUPT_STATUS: {
                GetIntStatusResponse_t report;
                std::memcpy(&report, &buf[1], sizeof(GetIntStatusResponse_t));
        
                emit intStatusReportReceived(report);
        
                break;
            }
        }
        

        If I look at the report object in debugger, it is correct each and every time. The problems surface once the struct is pushed towards QML with emit.

        1 Reply Last reply
        0
        • A Offline
          A Offline
          AnttiK
          wrote last edited by AnttiK
          #4

          I managed to stumble across a solution. My problem was that the header files provided by the first CMake project have to be explicitly added to the qt_add_executable stanza so that the meta-object compiler picks them up, and when I first tried it the variable I thought I read them to was, in fact, empty.

          This is why I received so many linker errors when using the ifdef __cplusplus in the header file. The shared header file was being used through an #import statement but for some reason meta-object compiler only processed the header file which included it, not the shared header itself.

          After noticing that the variable was empty I explicitly added the header files to qt_add_executable, and much to my surprise the linker error is now gone, and everything works.

          1 Reply Last reply
          1
          • A AnttiK has marked this topic as solved

          • Login

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved