Projekt wird trotz Parser-Fehlern compiliert. Warum?

Gast #5723817
Lesenswert?

Vorweg:
Die Kit-Einstellungen für QT-Creator auf meinem Ubuntu 18 mußte ich nach 
langem Hin und Her alle von Hand einstellen.

Das automatisch erzeugte Hallo-Welt funktioniert zwar, jedoch ist es 
voller Fehler. Warum funktioniert es trotzdem?
Angehängte Dateien:
Gast #5723859
Lesenswert?

Hast wohl recht. Alles deutet darauf hin, daß das Prob von meiner 
unsauberen Installation kommt.

Viele Fehler sind Forwärtsdeklarationen. Scheint so, als ob was 
Wichtiges fehlt.

Ich werd's nochmal neu installieren, das Geraffel.
Gast #5723973
Lesenswert?

Der Autor: Sven B. (scummos) Datum: 05.02.2019 11:53 schrieb:
>Liegt vermutlich eher an deiner Kit-Konfiguration.
Da hatte ich die größten Probleme. make, cmake und qmake hab ich in 
Verzweiflung mit Trial&Error probiert.

Jetzt sind fast alle Parser-Fehler weg, bis auf einen:


/home/joachim/Versuch/Test_1/mainwindow.h:12: warning: zero as null 
pointer constant
Leider ist auf dem Screenshot nicht der Schwebetext des Mauszeigers 
drauf, der sagt was von Nullabillity issue und
-Wzero-as-null-pointer-constant
Sieht nach nem Compilerflag aus. Weiß jemand, wo man die Flags setzt? 
Autodetect hat für C und C++ angeblich 2x8 Compiler gefunden; und ein 
Feld für Compiler-Flags sehe ich auch nicht wirklich.
Angehängte Dateien:
#5724049
Lesenswert?

Dr. Sommer schrieb:
> Und du solltest std::unique_ptr für "ui" verwenden :-)

Hmmh, nicht sicher, hast du das ausprobiert? Geht das mit einer Forward 
Declaration in diesem Fall?

Der Punkt an diesem merkwürdig wirkenden ui-Pointer-Idiom ist, dass die 
Definition der Struktur aus den uic-generierten ui_foo.h-Files kommt. 
Die möchte man aber nicht in seinen Headern #includen, weil man sie 
nicht installiert, d.h. ein Header (z.B. für ein Widget), der ein 
ui_foo.h-File braucht, kann nicht von anderen Projekten verwendet 
werden. Die Forward Declaration löst dieses Problem.
#5724050
Lesenswert?

Parser schrieb:
> Jetzt sind fast alle Parser-Fehler weg, bis auf einen:
>
>
> /home/joachim/Versuch/Test_1/mainwindow.h:12: warning: zero as null
> pointer constant

Ja, der QtCreator hat irgendwie in den neusten Versionen extrem 
pingelige Warning-Flags eingestellt, die jedes normale Stück Code mit 
einem See von Problemem übersäen. Schau mal in den Kit-Einstellungen, 
das kann man bestimmt irgendwo umstellen.

Das ist aber kein Fehler, sondern nur eine (vermutlich sogar korrekte) 
Warnung, die du auch ignorieren kannst.
Gast #5724053
Lesenswert?

Sven B. schrieb:
> Geht das mit einer Forward
> Declaration in diesem Fall?

So in der Art? Ja.
1
// -- Header
2
#include <memory>
3

4
namespace Ui {
5
  class MainWindow;
6
}
7

8
class MainWindow {
9
  public:
10
    MainWindow ();
11
  private:
12
    std::unique_ptr<Ui::MainWindow> ui;
13
};
14

15
// -- Autogenerierter Header
16
#include <iostream>
17
namespace Ui {
18
  class MainWindow {
19
    public:
20
      MainWindow () { std::cout << "Ui::MainWindow::MainWindow ()\n"; }
21
      ~MainWindow () { std::cout << "Ui::MainWindow::~MainWindow ()\n"; }
22
  };
23
}
24

25
// -- Source
26

27
MainWindow::MainWindow () : ui (new Ui::MainWindow ()) {
28
}
29

30
int main () {
31
  MainWindow mw;
32
}

Erspart den Destruktor und die Überlegung, ob jetzt "ui" wirklich immer 
unter allen Fällen korrekt gelöscht wird... Wäre "ui" ein einfacher 
Pointer, und würde man nach "ui" in der "class MainWindow" einen 
weiteren Member hinzufügen, dessen Konstruktor eine Exception wirft, 
hast du ein Memory-Leak.
Gast #5724125
Lesenswert?

Sven B. schrieb:
> Ich bin nicht ganz überzeugt, mit dem gcc 8 hier geht das, aber es wäre
> mal interessant welche Compiler das schlucken und welche nicht ;)
> Ich sehe hier das potentielle Problem, dass statisch der Destruktor
> eines vorwärtsdeklarierten Typs aufgerufen wird ...

Wo ist der Unterschied zum Aufruf von delete?
Gast #5724138
Lesenswert?

zu der lästigen Warnung:
-Wzero-as-null-pointer-constant
findsch im Makefile nix dazu.
1
MAKEFILE      = Makefile
2

3
####### Compiler, tools and options
4

5
CC            = gcc
6
CXX           = g++
7
DEFINES       = -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB
8
CFLAGS        = -pipe -g -Wall -W -D_REENTRANT -fPIE $(DEFINES)
9
CXXFLAGS      = -pipe -g -std=c++0x -Wall -W -D_REENTRANT -fPIE $(DEFINES)
10
INCPATH       = -I../../Qt/5.1.0/gcc_64/mkspecs/linux-g++ -I../Test_3 -I../../Qt/5.1.0/gcc_64/include -I../../Qt/5.1.0/gcc_64/include/QtWidge$
11
LINK          = g++
12
LFLAGS        = -Wl,-rpath,/home/joachim/Qt/5.1.0/gcc_64 -Wl,-rpath,/home/joachim/Qt/5.1.0/gcc_64/lib
13
LIBS          = $(SUBLIBS) -L/home/joachim/Qt//5.1.0/gcc_64/lib -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread 
14
AR            = ar cqs
15
RANLIB        = 
16
QMAKE         = /home/joachim/Qt/5.1.0/gcc_64/bin/qmake
17
TAR           = tar -cf
18
COMPRESS      = gzip -9f
19
COPY          = cp -f
20
SED           = sed
21
COPY_FILE     = cp -f
22
COPY_DIR      = cp -f -R
23
STRIP         = strip
24
INSTALL_FILE  = install -m 644 -p
25
INSTALL_DIR   = $(COPY_DIR)
26
...
27
... und so weiter
28
...

Wie geht man denn am besten vor um herauszufinden wo und wie das 
Makefile gemacht wird um den Schalter
-Wzero-as-null-pointer-constant
rauszunehmen?

Oder hat jemand ne bessere Strategie?
#5724906
Lesenswert?

mh schrieb:
> Sven B. schrieb:
>> Ich bin nicht ganz überzeugt, mit dem gcc 8 hier geht das, aber es wäre
>> mal interessant welche Compiler das schlucken und welche nicht ;)
>> Ich sehe hier das potentielle Problem, dass statisch der Destruktor
>> eines vorwärtsdeklarierten Typs aufgerufen wird ...
>
> Wo ist der Unterschied zum Aufruf von delete?

das steht im cpp-File, wo der Typ komplett bekannt ist
Gast #5724956
Lesenswert?

Sven B. schrieb:
> mh schrieb:
>> Sven B. schrieb:
>>> Ich bin nicht ganz überzeugt, mit dem gcc 8 hier geht das, aber es wäre
>>> mal interessant welche Compiler das schlucken und welche nicht ;)
>>> Ich sehe hier das potentielle Problem, dass statisch der Destruktor
>>> eines vorwärtsdeklarierten Typs aufgerufen wird ...
>>
>> Wo ist der Unterschied zum Aufruf von delete?
>
> das steht im cpp-File, wo der Typ komplett bekannt ist

Der Destruktor vom unique_ptr wird an der gleichen Stelle aufgerufen, 
was soll also anders sein?
Gast #5725009
Lesenswert?

Parser schrieb:

> Wie geht man denn am besten vor um herauszufinden wo und wie das
> Makefile gemacht wird um den Schalter
> -Wzero-as-null-pointer-constant
> rauszunehmen?

> CFLAGS        = -pipe -g -Wall -W -D_REENTRANT -fPIE $(DEFINES)
 Könnte es vom -Wall kommen?


> Oder hat jemand ne bessere Strategie?

Waroum nicht den code so anpassen das kein Warning getriggert wird?
#5725272
Lesenswert?

mh schrieb:
> Der Destruktor vom unique_ptr wird an der gleichen Stelle aufgerufen,
> was soll also anders sein?

Der Code für den automatisch generierten Destruktor steht im Header, und 
da ist der Typ unvollständig. Jetzt weiß ich auch, was man an dem 
Beispiel oben ändern muss, damit es nicht mehr geht: man verschiebe mal 
das main() in eine extra Datei. Dann bekommt man
1
g++ -o test test.cpp foo.cpp
2
In file included from /usr/include/c++/8.2.1/memory:80,
3
                 from test.h:1,
4
                 from foo.h:1,
5
                 from foo.cpp:1:
6
/usr/include/c++/8.2.1/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Ui::MainWindow]':
7
/usr/include/c++/8.2.1/bits/unique_ptr.h:274:17:   required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Ui::MainWindow; _Dp = std::default_delete<Ui::MainWindow>]'
8
test.h:7:7:   required from here
9
/usr/include/c++/8.2.1/bits/unique_ptr.h:79:16: error: invalid application of 'sizeof' to incomplete type 'Ui::MainWindow'
10
  static_assert(sizeof(_Tp)>0,
11
                ^~~~~~~~~~~

wie erwartet.

Was man aber machen kann, ist einen Destruktor deklarieren und im cpp 
definieren (als leer), dann geht es.
Gast #5725427
Lesenswert?

Sven B. schrieb:
> mh schrieb:
>> Der Destruktor vom unique_ptr wird an der gleichen Stelle aufgerufen,
>> was soll also anders sein?
>
> Der Code für den automatisch generierten Destruktor steht im Header, und
> da ist der Typ unvollständig.

Mit "Code" meinst du in diesem Fall ein class template.

Sven B. schrieb:
> Jetzt weiß ich auch, was man an dem
> Beispiel oben ändern muss, damit es nicht mehr geht: man verschiebe mal
> das main() in eine extra Datei. Dann bekommt man
> g++ -o test test.cpp foo.cpp
> In file included from /usr/include/c++/8.2.1/memory:80,
>                  from test.h:1,
>                  from foo.h:1,
>                  from foo.cpp:1:
> /usr/include/c++/8.2.1/bits/unique_ptr.h: In instantiation of 'void
> std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp =
> Ui::MainWindow]':
> /usr/include/c++/8.2.1/bits/unique_ptr.h:274:17:   required from
> 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Ui::MainWindow;
> _Dp = std::default_delete<Ui::MainWindow>]'
> test.h:7:7:   required from here
> /usr/include/c++/8.2.1/bits/unique_ptr.h:79:16: error: invalid
> application of 'sizeof' to incomplete type 'Ui::MainWindow'
>   static_assert(sizeof(_Tp)>0,
>                 ^~~~~~~~~~~
>
> wie erwartet.
>
> Was man aber machen kann, ist einen Destruktor deklarieren und im cpp
> definieren (als leer), dann geht es.

Der Typ muss, wie erwartet, vollständig bekannt sein, wenn der 
Destruktor vom unique_ptr instaziiert wird. An diesem Punkt muss der Typ 
aber auch für delete bekannt sein (nicht immer, aber man ist im 
Grenzgebiet von "undefiend behaviour land"), wenn man keinen unique_ptr 
nutzt.
#5725729
Lesenswert?

mh schrieb:
> Der Typ muss, wie erwartet, vollständig bekannt sein, wenn der
> Destruktor vom unique_ptr instaziiert wird. An diesem Punkt muss der Typ
> aber auch für delete bekannt sein (nicht immer, aber man ist im
> Grenzgebiet von "undefiend behaviour land"), wenn man keinen unique_ptr
> nutzt.

Ja. Aber: so wie es oben im Beispiel aufgeschrieben ist, funktioniert es 
i.A. nicht, weil das nicht der Fall ist. Eben weil das für den 
automatisch erzeugten Default-Destruktor nicht gilt.
Gast #5725837
Lesenswert?

Sven B. schrieb:
> mh schrieb:
>> Der Typ muss, wie erwartet, vollständig bekannt sein, wenn der
>> Destruktor vom unique_ptr instaziiert wird. An diesem Punkt muss der Typ
>> aber auch für delete bekannt sein (nicht immer, aber man ist im
>> Grenzgebiet von "undefiend behaviour land"), wenn man keinen unique_ptr
>> nutzt.
>
> Ja. Aber: so wie es oben im Beispiel aufgeschrieben ist, funktioniert es
> i.A. nicht, weil das nicht der Fall ist. Eben weil das für den
> automatisch erzeugten Default-Destruktor nicht gilt.

Was ist nicht der Fall? Was gilt nicht für den Default-Destruktor?

Ich gehe davon aus, dass der Typ vollständig bekannt ist, da sonst ein 
delete gefährlich ist. Wenn der Typ vollständig bekannt ist kann man 
auch unique_ptr inkl. Destruktor instanzieren.
#5725844
Lesenswert?

mh schrieb:
>> Ja. Aber: so wie es oben im Beispiel aufgeschrieben ist, funktioniert es
>> i.A. nicht, weil das nicht der Fall ist. Eben weil das für den
>> automatisch erzeugten Default-Destruktor nicht gilt.
>
> Was ist nicht der Fall? Was gilt nicht für den Default-Destruktor?

Probier's doch einfach aus, tu das main() in eine eigene cpp-Datei, in 
der du nur den Header und nicht das auto-genierte inkludierst und 
bewundere den Compilerfehler.

Der Default-Destruktor wird in der .h-Datei generiert, den explizit 
hingeschriebenen kann man hingegen in die .cpp-Datei schreiben. In 
ersterer ist der Typ unvollständig, und deshalb kompiliert es mit dem 
Default-Destruktor nicht.

Das Beispiel funktioniert nur, weil die einzige Verwendung des 
Default-Destruktors in der Compilation Unit ist, wo auch der 
auto-generierte Code inkludiert wird. Teile es in zwei Compilation Units 
auf, und es geht nicht mehr.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren