C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Alles rund um die Programmierung mit Qt
Antworten
Taneeda
Beiträge: 6
Registriert: 18. Oktober 2017 13:45
Wohnort: Rastede

C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von Taneeda »

Moin zusammen,

ich habe eine recht spezielle Frage zu Qt, im Zusammenhang mit der Integration eines C-Code-Projekt in einem Qt C++ Projekt. Da der Code recht umfangreich ist, sehe ich aktuelle keine Möglichkeit für ein Fallbeispiel, das Prinzip ist aber eigentlich schnell erklärt, so dass ich erstmal hoffe, dass wir auf dieser theoretischen Ebene weiterkommen :)

Im besagten QT 5.8 Projekt integriere ich Bestandscode eines älteren C-Code-Projekt. Der Code wird ausgeführt und verarbeitet wie erwartet, soweit so gut. Jetzt ist es so, dass ich mir gerne in der Qt GUI die Werte von Variablen aus dem C-Code (welche über extern ... verfügbar sind, also global Variablen aus Sicht des C-Codes) in der GUI anzeigen lassen möchte. Einfache Verfahren wie Debuggen oder explizit die Werte abprüfen funktionieren natürlich und nutze ich aktuell auch. Für die Zukunft suche ich allerdings ein möglichst dynamisches System, wo ich nicht viele Änderungen mehr am C++ QT Code machen muss.

Aktuell erscheint mir das QT Properties System (QProperty) sehr vielversprechend, allerdings habe ich keine Erfahrungen damit. Allerdings frage ich mich allerdings, wie ich die Variablen aus dem C-Code möglichst generisch in dieses System rein bekomme :/

Kann hierbei jemand weiterhelfen? Was sind eure Ideen?

Gruß Ronald
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von Christian81 »

Da Du keine Infos hast wann sich die Variablen ändern würde ich einfach alle n Sekunden pollen und diese dann einfach mittels eines QLabel darstellen.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Taneeda
Beiträge: 6
Registriert: 18. Oktober 2017 13:45
Wohnort: Rastede

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von Taneeda »

So ähnlich mach ich das momentan auch. Da der C-Code regelmäßig ausgeführt wird, kann ich da immer wieder prüfen, ob sich in der gewünschten Variable was geändert hat...

Aktuell habe ich eine Klasse erzeugt "DataObserver" welcher in einen Zeiger auf die gewünschte Variable übergebe. Innerhalb der Klasse prüfe ich dann auf Änderung und löse im Falle einer Änderungen ein entsprechendes Signal aus, welches ich bis zur GUI durchleite und dann entsprechend den aktualisierten Wert anzeige.

Ich frage mich aktuell aber, ob und falls ja welche Möglichkeiten es gibt die gewünschten DataObserver nicht direkt im Code fix zu definieren, sondern eher zur Laufzeit dynamisch auszuwählen. GIbt es eine Möglichkeit den C-Code auch mit dem MOC zu verarbeiten und darüber auf die mit "extern" deklarierten Variablen über das Property-System zuzugreifen? Oder andere/ähnliche Möglichkeiten?

Ich stelle mir aktuell vor, dass ich wahrscheinlich nicht drum herum kommen werde wenigsten alle möglichen "beobachtbaren" Variablen zu registrieren, damit diese später aus der GUI ausgewählt werden können. Keine Ahnung, wie der C++ Code sonst wissen soll, dass es die jeweiligen Variablen überhaupt gibt und zugreifbar sind :/ Diesen Schritt könnte man aber wenigstens einigermaßen per Skript vorab automatisieren und so den Code in das C++ Projekt schleusen ohne das alles von Hand machen zu müssen.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von Christian81 »

Was anderes würde mir auch nicht einfallen - ist aber zum Glück ja nur Einmalaufwand. Wenn man den Zugriff auf je eine Variable gut in einer eigenen kleinen Klasse kapselt, sollte das auch nicht so viel Schreibaufwand sein.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Taneeda
Beiträge: 6
Registriert: 18. Oktober 2017 13:45
Wohnort: Rastede

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von Taneeda »

Der eigentliche Aufwand liegt in den zukünftigen Änderungen am C-Code, wenn weitere Variablen hinzukommen, ober bestehende Variablen gelöscht werden, dann muss man auch wieder den C++ Code anfassen.

Im Anhang habe ich eine zip-Datei mit Beispielcode angehangen, damit habe ich mal (auf die Schnelle eben) versucht den Grundgedanken darzustellen...
CCodeTest.zip
(5.42 KiB) 264-mal heruntergeladen
Nachdem ich nur ein bisschen mit dem Qt-Property-System herumgespielt habe, anbei ein erster Schritt in die beschriebene Richtung... Stellt sich nun die Frage, wie man am möglichst automatisch die "extern" deklarierten Variablen auf die Qt Seite in das Property System bekommt, dazu hab ich aktuell noch keine Idee :/
CCodeTest (2).zip
(3.17 KiB) 262-mal heruntergeladen
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von odt »

Ich glaube, Reflection mit C wird kaum möglich sein. Auch der moc wird Dir wohl nicht helfen.

Hast Du schon mal daran gedacht, den Bestandescode selber zu parsen und den Code (deklarieren und registrieren) für externen Variablen zu generieren?

Vermutlich würde ich in diesem Fall auf das Property-System verzichten (viel Aufwand für Nichts). Die Idee mit den Zeigern auf die Variablen finde ich gut!

Viele Grüsse
Reto
ODT Informatik GmbH, Reto Tschofenig
Taneeda
Beiträge: 6
Registriert: 18. Oktober 2017 13:45
Wohnort: Rastede

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von Taneeda »

ja, den C-Code zu parsen ist der aktuelle Plan. Momentan sehe ich da einfach keine Alternative...

Worin genau siehst du hier den Nachteil beim Property-System? Wahrscheinlich übersehe ich nur was, da ich mit dem System noch keine praktischen Erfahrungen habe...

Aktuell sehe ich das Property System als Vorteil an, da ich ein "Property" mit einem beliebigen Bezeichner erzeugen und diesem die jeweiligen Zugriffsfunktionen (READ, WRITE) einfach zuweisen kann. Die externen Zugriffe laufen dann transparent über den Bezeichner-Namen. Ich wähle z.B. das gewünschte Property über eine ComboBox in der GUI aus (Auflistung aller Bezeichner) und kann die Werte dann ebenfalls über den selektieren Bezeichner über property()/setProperty() anpassen. Geht das noch einfacher? Wäre ja super, je einfacher desto besser :)
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von odt »

Zur Präzisierung: Ich würde auf das Qt-Property-System verzichten (also je Variable ein Q_PROPERTY zu machen, wodurch Du mehr Code generieren müsstest) sondern quasi ein eigenes, spezifisches implementieren.

Code: Alles auswählen

class Variable : public QObject
{
Q_OBJECT
private: 
  QString _name;
  int* _pointerToVariable;
  int _prevValue;
public:
  explicit Variable( QString name, int* pointerToVariable );
signals:
   void changed( int newValue );
public slots:
  void refresh(){ 
    int actValue = (*_pointerToVariable);
    if( actValue != _prevValue ){
      _prevValue = actValue; 
      emit changed( actValue );
    }
};
ODT Informatik GmbH, Reto Tschofenig
Taneeda
Beiträge: 6
Registriert: 18. Oktober 2017 13:45
Wohnort: Rastede

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von Taneeda »

Ah, ok, verstehe was du meinst. Aktuell mache ich ziemlich genau das :) Die Klasse heißt bei mir DataObserver... Zur Identifizierung der Variablen innerhalb der C++ Umgebung benutze ich ein enum-Typ, da war mein Gedanke irgendwann Richtung QString zu wechseln, einfach wegen einem schönen Bezeichner.

Über TIpps zur Verbesserung des Codes freue ich mich, bin immer für eine gute Diskussion zu haben :)

dataids.h

Code: Alles auswählen

#ifndef DATAIDS_H
#define DATAIDS_H

typedef enum
{
    eDATA_ID_VOLTAGE_SOURCE,
    eDATA_ID_ENGINE_SPEED,
    eDATA_ID_TRANS_OUT_SHAFT_SPEED,
    eDATA_ID_OPTION_T4I_OR_T4F,
    eDATA_ID_SCM_NODE_STATUS,
    eDATA_ID_CRANE_OP_MODE,
    eDATA_ID_CC_STATE,
    eDATA_ID_CARRIER_EMERGENCY_OFF_BTN,
    eDATA_ID_OPTION_AMOT_SYSTEM,
    eDATA_ID_CO_BUFF_CCM11_TO_CDM_WRITE_SDO,
    eDATA_ID_CARRIER_GEARBOX_AIR_PRESSURE_KPA,
    eDATA_ID_COMPONENT_SWITCH_KEYON,
    eDATA_ID_COMPONENT_SWITCH_KEYACC,
    eDATA_ID_ERR_VAL_AIR_PRESS_VALVE_TCPTO_PTO_ON,
    eDATA_ID_COUNT
} E_DataId;

#endif // DATAIDS_H
Aktuell bin ich mit der Implementierung noch nicht zufrieden, da ich für verschiedene Datentypen verschiedene Konstruktoren vorsehe, ich denke mal das geht besser, irgendwie über Templates vermute ich, da fehlt mir aber noch die Erfahrung...

DataObserver.h

Code: Alles auswählen

#ifndef DATAOBSERVER_H
#define DATAOBSERVER_H

#include <QObject>
#include <QDebug>
#include <QVariant>

#include "dataids.h"

class DataObserver : public QObject
{
    Q_OBJECT

    public:
        explicit DataObserver(E_DataId eDataId, QMetaType::Type dataType, quint8 *pu8Data, QObject *parent = 0);
        explicit DataObserver(E_DataId eDataId, QMetaType::Type dataType, quint16 *pu16Data, QObject *parent = 0);
        explicit DataObserver(E_DataId eDataId, QMetaType::Type dataType, qint16 *ps16Data, QObject *parent = 0);

        void checkForChange();
        bool isDataChanged();
        E_DataId getDataId();
        QMetaType::Type getDataType();
        QVariant getData();

    signals:
        void dataChanged(E_DataId eDataId, QMetaType::Type dataType, QVariant newData);

    public slots:

    private:
        bool m_startup;
        E_DataId eDataId;
        QMetaType::Type dataType;
        QVariant curData;
        QVariant lastData;

//        // Use UNION to allow only one variable (to save memory)
//        union {
            quint8 *pu8Data;
            quint16 *pu16Data;
            qint16 *ps16Data;
//        };

        void init(E_DataId eDataId, QMetaType::Type dataType);
};

#endif // DATAOBSERVER_H
Wie man sieht hatte ich mal vor die verschiedenen Datentypen als union zusammenzufassen, da ja stets nur ein Datentyp belegt ist. Hatte das einmal kurz getestet und mir sind auch weiter keine Probleme aufgefallen, war mir dann aber nicht sicher, ob das wirklich korrekt ist, da wollte ich mich erst noch genauer informieren.

DataObserver.cpp

Code: Alles auswählen

#include "dataobserver.h"

DataObserver::DataObserver(E_DataId eDataId, QMetaType::Type dataType, quint8 *pu8Data, QObject *parent)
    : QObject(parent)
{
    qInfo() << "New DataObserver 1 - " << QString::number(eDataId);

    init(eDataId, dataType);

    this->pu8Data = pu8Data;
    this->lastData.setValue(*pu8Data);
}

DataObserver::DataObserver(E_DataId eDataId, QMetaType::Type dataType, quint16 *pu16Data, QObject *parent)
    : QObject(parent)
{
    qInfo() << "New DataObserver 2 - " << QString::number(eDataId);

    init(eDataId, dataType);

    this->pu16Data = pu16Data;
    this->lastData.setValue(*pu16Data);
}

DataObserver::DataObserver(E_DataId eDataId, QMetaType::Type dataType, qint16 *ps16Data, QObject *parent)
    : QObject(parent)
{
    qInfo() << "New DataObserver 3 - " << QString::number(eDataId);

    init(eDataId, dataType);

    this->ps16Data = ps16Data;
    this->lastData.setValue(*ps16Data);
}

void DataObserver::checkForChange()
{
    // Check for data type
    if(nullptr != this->pu8Data)
    {
        this->curData.setValue(*this->pu8Data);
    }
    else if(nullptr != this->pu16Data)
    {
        this->curData.setValue(*this->pu16Data);
    }
    else if(nullptr != this->ps16Data)
    {
        this->curData.setValue(*this->ps16Data);
    }
    else
    {
        // Do nothing
    }

    // Check for data change
    if(     (this->lastData != this->curData)
        ||  (this->m_startup)
    ) // endif
    {
        this->lastData = this->curData;
        emit dataChanged(this->eDataId, this->dataType, this->curData);
    }
    else
    {
        // Do nothing
    }

    this->m_startup = false;
}

E_DataId DataObserver::getDataId()
{
    return this->eDataId;
}

QMetaType::Type DataObserver::getDataType()
{
    return this->dataType;
}

QVariant DataObserver::getData()
{
    return this->curData;
}

void DataObserver::init(E_DataId eDataId, QMetaType::Type dataType)
{
    this->m_startup = true;
    this->eDataId = eDataId;
    this->dataType = dataType;
    this->curData.setValue(0);

    this->pu8Data = nullptr;
    this->pu16Data = nullptr;
    this->ps16Data = nullptr;
}
Generelle Frage bezüglich dem Plan des Parsens der C-Code Dateien: Könnt ihr dazu gängige Tools oder Parser empfehlen? Habe bisher C-Code noch nie in so einer Form geparst. Werde noch selber nach Möglichkeiten forschen, aber fragen schadet ja nicht :)
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von odt »

Ich glaube, mit Q_ENUM kannst Du ein Wert in einen String wandeln. https://stackoverflow.com/questions/342 ... to-qstring

templates und QObject passen meines Wissens nicht direkt zusammen, d.h. ein von QObject abgeleitete Template-Klasse kann keine signals/slots haben. Da würde ich nicht zu viel Zeit verlieren und je Datentyp eine Ableitung machen (FloatDataObserver, IntDataObserver jeweils von DataObserver abgeleitet).

Ah, erst jetzt gesehen, dass Du die verschiedenen Typen via union und einem grossen if in einer Klasse lösen möchtest. Dies ist möglich, aber ich würde je Typ eine Klasse machen.
PS: Den Datentyp müsstest Du nicht als Parameter im Konstruktor übergeben, da der Pointer-Typ den Datentyp ja bereits impliziert. Im Else-Fall gehört meiner Meinung noch ein Q_ASSERT( false ) nach // Do nothing

Als ich vor ein paar Jahren Code parsen musste, entschied ich mich für eine Quick-and-dirty-Variante: Gehe Verzeichnis durch, jede cpp/h-Datei als Text einlesen, Splitten nach Spaces, Klammern und dann etwas if-Magie. Dann kam noch dies und jenes hinzu. Heute bin ich bei rund 30 Klassen gelandet, die meinen Source-Code als Modell abbilden, bei fremden Code aber scheitern, weil ich eben nicht alle Konstellationen berücksichtigt habe. Zwischendurch haben wir mal evaluiert, den Code mit clang zu parsen. Zumindest damals war es kompliziert (komplexe Laufzeit-Umgebung, eingeschränkte Portabilität, komplizierte Anwendung, Abhängigkeiten und unerklärliche Fehler). Auch ein Ast-Ansatz war nicht sehr erfolgsversprechend. Welche Lösungsvariante für Dich am Besten ist, hängt von Deiner Erfahrung und dem Aufbau des Bestandescodes ab. Plane für das Code-Parsen genug Zeit ein!
ODT Informatik GmbH, Reto Tschofenig
Taneeda
Beiträge: 6
Registriert: 18. Oktober 2017 13:45
Wohnort: Rastede

Re: C-Code in Qt/C++ Projekt - Variablen aus C-Code zur Laufzeit zum Ansehen auswählen

Beitrag von Taneeda »

Q_ENUM wäre eine Möglichkeit, das wollte ich mir irgnedwann noch genauer ansehen

Bei den Templates wollte ich seinerzeit noch nicht aufgeben, weswegen ich die Idee noch nicht verworfen hatte. Zwischenzeitlich habe ich aber auch in der offiziellen Qt Doku gelesen, dass dies nicht möglich ist, Info dazu auch https://forum.qt.io/topic/13692/templat ... y-q_object

Deine Idee klingt auch gut, mit DataObserver als Basisklasse, werde ich mir genauer ansehen :)

Ja, ein Parser, denke auch, dass das ganz schön aufwändig und knifflig wird, weswegen ich da eigentlich gar nicht selbst Hand anlegen möchte... Die grundlegende Herausforderung sehe ich eben auch in den verschiedenen Möglichkeiten. Sind prinzipiell nicht nur Variablen, auf die ich zugreifen möchte (auch wenn das allermeistens der Fall ist). Prinzipiell möchte ich auch auf Funktionen zugreifen, die ihrerseits ja die wildestens Signaturen haben können :)

Hmm, Grundüberlegung zum Parser:
1. Text nach ";" teilen
2. Jeden Teil über RegEx prüfen
3. Unterscheidung ob Funktionsignatur oder Variablendeklaration
4. Code generieren für DataObserver

Klingt erstmal überschaubar, aber wie immer steckt der Teufel ja im Details und sowieso kommt immer alles ganz anders und als man denkt :D Bin schon gespannt wie das wird, hehe
Antworten