[C++] Mehrere Header-Dateien Fehler?

5 Antworten

Vermutlich gibt es irgendwo bereits eine Funktion "setColor"

Wenn Du C++ programmierst, solltest Du die Möglichkeiten dieser Sprache auch nutzen. Es ist z.B. guter Stil, namespaces zu verwenden (z.B. "consoleHelpers"), dann kann es keine Namenskollisionen mit Funktionen aus dem Framework geben.

Dann sollte man nur die includes einfügen, die auch verwendet werden, in tile.h ist der include überflüssig.

Weiterhin ist (m.E.) die Verwendung von Include-Guards per #define fehleranfällig, es gibt "#pragma once", das exakt dasselbe tut.

Herangehensweise:

Da dem Compiler offenbar die Funktion setColor nicht schmeckt, benennst Du die vorübergehend (z.B. in "setForegroundAndBackgroundColor" um). Es ist zumindest bemerkenswert, dass die enum nicht angemault wird.

Also soweit ich sehe ist das GUARDING korrekt, zumindest sehe ich keinen augenscheinlichen Fehler.

Als stilistische Anmerkung:

Auch in eigenen Headern sollten vorzugsweise nur Deklarationen stehen.

----

Ist der Fehler denn vollständig, also exakter Wortlaut?

Welchen Compiler nutzt Du?

Vergessen, Du könntest dem Compiler noch mitteilen, daß er die Verbosity erhöht, falls möglich.

Also soweit ich sehe ist das GUARDING korrekt, zumindest sehe ich keinen augenscheinlichen Fehler.

Einen kleinen Schönheitsfehler gibt es allerdings: Bezeichner, die mit einem Unterstrich, gefolgt von einem Großbuchstaben beginnen, sind grundsätzlich als reserviert anzusehen und werden von Tools zur statischen Codeanalyse auch moniert werden.

Leider sieht man in sehr vielen Lehrbüchern und Tutorials (gefühlt die ca. Hälfte), dass Include-Guards mit Unterstrich und Großbuchstaben beginnen, was man aber laut Standard nicht tun sollte.

Grundsätzlich sind auch Bezeichner reserviert, die zwei oder mehr aufeinanderfolgende Unterstriche enthalten. Im globalen Namensraum sind außerdem Bezeichner reserviert, die mit einem Unterstrich gefolgt von einem Kleinbuchstaben beginnen (laut C++ Standard), oder auf _t enden (laut POSIX).

POSIX definiert noch viel mehr Regeln, die über den "normalen" Standard hinaus gehen, aber die würden hier den Rahmen sprengen und die oben genannten sind bei weitem die wichtigsten.

Beispiele:

Foobar // gut
_Foobar // schlecht
Foo_bar // gut
FOOBAR // gut
FOO_BAR // gut
_FOOBAR // schlecht
FOOBAR_ // gut
FOO_BAR_ // gut
FOO__BAR // schlecht
__FOO__BAR__ // schlecht
Foo__bar // schlecht
__Foobar // schlecht
Foobar_ // gut
_Foobar_ // schlecht
__Foo__bar__ // schlecht
_foobar // schlecht
foobar_ // gut
foobar__ // schlecht
foobar_t // schlecht
foobar // gut

So, das wars ... :)

PS: Das kann man alles ignorieren, wenn man in seinem stillen Kämmerlein entwickelt, aber sobald man zukunftssicheren und wartbaren Code oder eine Bibliothek schreibt, die Dritte zu Gesicht bekommen, bzw. die in fremden Projekten landet, sollte man sich unbedingt so strikt wie nur möglich an die Spezifikationen halten. :)

3
@TeeTier

Interessant, so ziemlich alle Bibliotheken nutzen _HEADERNAME_H oder gar __HEADERNAME_H - so zumindest bei Linux.

https://gcc.gnu.org/onlinedocs/cpp/Once-Only-Headers.html#Once-Only-Headers

Das ist was bei gcc vorgeschlagen wird, sie votieren auch auf anfängliche Unterstriche bei lokalen Headern zu verzichten, um Konflikte mit Includes der Bibliotheken zu vermeiden. Insofern wäre FILENAME_H wohl akzeptabel.

Im C-Standard habe ich auf die Schnelle nichts gefunden.

Solange es nicht zu einem Konflikt kommt, müßte das Guarding allerdings trotzdem funktionieren.

Die Frage ist (auch bei POSIX) was sind bindende Regeln und was sind Konventionen, da muß man oft sehr genau hinschauen.

2
@KarlRanseierIII

Ganz am Ende in einem Absatz hinter deinem Link schreiben die GNU-Leute zwar, dass man "_FOO" vermeiden soll, aber dass "__FOO" OK ist ... was natürlich gegen den Standard verstößt, und was die GCC-Leute eigentlich wissen müssten ... oder ich bin gerade zu müde, um einen simplen englischen Satz verstehen zu können. :)

Hier ist noch ein Link, hinter dem die Top-Antwort einige der Regeln (nicht alle!) ganz gut zusammen fasst:

https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier/228848

Das bezieht sich auf C++, aber für C habe ich jetzt keinen Link zur Hand, sondern nur die PDF des letzten öffentlich verfügbaren Drafts zu C11, und darin heißt es in Punkt 6.10.8.2 auf Seite 193:

None of these macro names [...] shall be the subject of a #define or a #undef preprocessing directive. Any other predefined macro names shall begin with a leading underscore followed by an uppercase letter or a second underscore.

Das bezieht sich auf vordefinierte Makros, also nicht wundern! Implizit steht dort also, dass nur vordefinierte Makros mit Unterstrich gefolgt von Großbuchstaben oder weiteren Unterstrichen definiert werden sollen ... der Absatz richtet sich an Compilerbauer.

Und in 7.1.3.1 auf Seite 200 steht ganz klar und eindeutig:

 All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.

... und weiter:

All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

Bei POSIX-Konventionen hast du natürlich Recht, aber obige Regeln sollte man schon als bindend ansehen.

Damit wäre die Sache klar, oder? :)

1
@KarlRanseierIII

Noch ein Nachtrag zu POSIX und typedefs, die auf "_t" enden: Es ist extrem nervig, wenn zwei Bibliotheken len_t oder id_t oder ähnliche naheliegende Typen definieren.

Bibliotheken wie ALSA machen das zwar auch gewissermaßen "falsch", aber immerhin haben deren Typen lange Namen und Präfixe, sodass Dinge wie snd_seq_addr_t nicht kollidieren werden.

Ein addr_t alleine wird aber garantiert mit einer ganzen Reihe anderer Bibliotheken kollidieren ... naja, richtige namespaces sind eben ein großer Teil von C++ ... bei C muss man immer ellenlange Präfixe nutzen. ><

1
@TeeTier

Laut anderen Quellen lautet der Satz are reserved for any use by the implementation.

Sie wären also der Implementierung des Compilers vorbehalten und ihre Nutzung bettelt nach Konflikten.

Insofern stimme ich zu, man sollte es unterlassen, wie gesagt:

FILENAME_H oder auch _filename_h_ sind ja brauchbare Alternativen.

0
@TeeTier

Ich kann bei C, solange ich es nicht als Interface benötige, auch die Sichtbarkeit steuern, insofern kann ich Namen, die zur Kollission neigen könnten auf die Translation-Unit beschränken, sie tauchen dann auch nicht als globale Objektnamen auf.

Man sollte eigentlich denken, daß es Anno 2018 zum Standardrepertoire gehört, interne Utility-Functions etc. als nicht sichbar zu markieren. Aber ja, die Konfliktvermeidung ohne explizite Namespaces ist ein Riesenspaß.

0
@KarlRanseierIII

Ich habe hier noch ein Zitat zu reservierten Namen aus der gcc Dokumentation:

In addition to the names documented in this manual, reserved names include all external identifiers (global functions and variables) that begin with an underscore (‘_’) and all identifiers regardless of use that begin with either two underscores or an underscore followed by a capital letter are reserved names. This is so that the library and header files can define functions, variables, and macros for internal purposes without risk of conflict with names in user programs.

Das erklärt dann auch das Guarding in den Bibliotheksheadern.

1
@KarlRanseierIII

Ich habe es direkt aus der Primärquelle, also dem offiziellen Draft, kopiert. Kann natürlich auch sein, dass es im Standard leicht anders formuliert ist, aber ich will da jetzt kein Geld für ausgeben. :)

Und ob "_filename_h_" jetzt so OK ist, hängt davon ab, ob mit "file scope" ein "translation unit scope" oder tatsächlich nur die Datei gemeint ist.

Ich nutze in meinen Projekten immer so etwas wie ...

FIRMA_PROJEKT_MODUL_DATEI_H

... wobei ich möglichst darauf achte, nicht die für C89 definierte Bezeichnerlänge zu überschreiten. Manchmal setze ich auch noch ein "GUARD_" davor ... je nach Projektkonventionen.

Am besten einfach grundsätzlich immer alles vermeiden, was mit einem Unterstrich beginnt, bzw. mehrere davon aufeinanderfolgend enthält. Dann ist man garantiert auf der sicheren Seite ... vorausgesetzt, man nutzt sinnvolle Präfixe für Bibliotheken. :)

0
@KarlRanseierIII
Das erklärt dann auch das Guarding in den Bibliotheksheadern.

Naja, für die Compilerbauer ist das dann eben einfach nur "implementation defined", und da es ja ihre eigene Implementierung ist, auch völlig OK.

Für uns Otto-Normalbürger hingegen wäre das "undefined behavior" und sollte deshalb ein Nogo sein. :)

Aber wie du schon anfangs sagtest, macht das wirklich ein sehr großer Teil aller Entwickler falsch. Ich schätze locker 30% bis 50% aller Projekte.

Das gleiche Problem gibts ja beim Casten des zurück gegebenen void-Zeigers von malloc() ... dabei ist vielen ja auch nicht klar, wann man es tun, und wann man es möglichst vermeiden sollte. Aber naja, jede Sprache hat so ihre Eigenheiten, und bei genauem Hinsehen, ergeben die meisten davon auch einen Sinn, auch wenn sich dieser auf den ersten Blick oft nicht gleich erschließt. :)

0
@TeeTier

Vielleicht liegt das Problem auch darin, daß viele Leute, wenn sie sich unsicher sind, eben in Systemheadern o.ä. schauen, getreu dem Motto, die Jungs müßten ja wissen, wie man es richtig macht.

Zack wird das Guarding kopiert und man hat den Salat.

Aber auch immer wieder schön:

Also, Jungs, die Verabeitung von Signalen in Eurem Programm ist unsicher und nicht Standardkonform. Der Asynchrone Handler sollte nur globale Objekte anfassen, die atomar änderbar sind, um undefinierte Inhalte und darauf basierend undefiniertes Verhalten zu vermeiden. Auch die Systemcalls (Liste) haben im Signalhandler nichts verloren, weil sie nicht reentrant sind und sie nicht in der vom Standard vorgegebenen Liste von erlaubten Syscalls steht.

Antwort dann:

... wie? Egal! Funktioniert doch, also was solls!

Da kann man dann eben nur noch mit der Schulter zucken.

1
@KarlRanseierIII

Oh ja, das sieht man leider auch unglaublich oft. Da wird ein ellenlanger Schwanz an Code mit X Seiteneffekten (sprich nicht-atomar) in die Handler gepackt, und dann wundert man sich, dass es zu Heisenbugs kommt. :)

0

Ich möchte meinen, es liegt an der Definition der Funktion in der Header-File (statt einer Deklaration).

Entweder du verschiebst die Funktion in eine Implementationsdatei (cpp), wo sie eigentlich auch hingehört oder du setzt einmal das Schlüsselwort inline vor die Definition:

inline void setColor(Color fg, Color bg) {
  SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), bg * 16 + fg);
}  

Ich denke auch, dass es daran liegt, da Funktionen ja normalerweise die externe Speicherklasse haben.

Anstatt "inline" geht ja auch "static" (was man bei C++ aber lieber durch anonyme namespaces ersetzen sollte) oder "constexpr" nutzen (im obigen Code zwar nicht, aber bei konstanten Funktionen schon).

Auf jeden Fall bin ich mir sicher, dass du mit deiner Vermutung recht hast. :)

0
@TeeTier

Vermutlich kannst du pixelinos Frage:

Aber ist es jetzt performanter ob ich es static oder inline habe?

etwas besser beantworten als ich?

0

Das dürfte (IMHO) keine Relevanz haben. <hfile> wird für Systemheader verwendet, die nichtmal als Datei vorliegen müssen, es wird dem Implementierer des Compilers überlassen.

Include mit "filename" gelten als normale Quellcode-Imports direkt in die Datei in der das include steht. Der Präprozessor ersetzt also das Inlcude mit dem Inhalt der angegebenen Datei inplace.

Stilistisch ist das natürlich trotzdem eher unschön, es dürfte aber eigentlich nicht zu einem Fehler führen.

0

Danke! Du hattest wiedermal Recht. Aber es hat mich gewundert, weil es, ohne die Tile-Klasse hat es noch funktioniert. Ich habe jetzt bei jeder Funktion in dem Header inline davor geschrieben und es funktioniert einwandfrei.

Aber ist es jetzt performanter ob ich es static oder inline habe?

Werde mich in näherer Zukunft sowieso damit beschäftigen, aber soweit bin ich noch nicht, darum frage ich jetzt einfach mal so :D

0
@pixelino

Ja, weil zu diesem Zeitpunkt die Funktion auch noch nur einmal eingebunden wurde. Bei den 2 Includes hingegen wird je Include die Funktion setColor in die jeweilige Objektdatei eingebunden. Dies wird vom Linker erst bemerkt, wenn er versucht, die erzeugten Objektdateien (Tile.o und main.o) miteinander zu verbinden. Er gerät also in einen Definitionskonflikt.

Aber ist es jetzt performanter ob ich es static oder inline habe?

Hier gilt zuerst einmal die Antwort, die dir TeeTier dazu bereits gegeben hat. Statt static sollte wenn, eher ein Namespace verwendet werden. Lies dazu hier (Abschnitt Conclusion) mehr.

Zu der Performance-Frage an sich kann ich nicht so viel sagen. Das Schlüsselwort inline suggeriert dem Compiler, dass er dort, wo die Funktion aufgerufen wird, den Code auch ruhig direkt einfügen kann (halte diese Funktionen daher möglichst klein; übrigens entscheidet der Compiler bei Funktionen die Handhabung normalerweise selbst, egal ob du inline davorsetzt oder nicht). Bei einer Lösung mit static/Namespace sollte er stattdessen alle Definitionen mit aufnehmen und sie jeweils nur lokal zur Verfügung stellen. Somit hast du bei inline vermutlich einen geringen Performance-Vorteil, aber ich denke nicht, dass dieser sonderlich erwähnenswert sein wird. Ich denke, dass es an dieser Stelle sinnvoll wäre, noch einen weiteren Kommentar einzuholen.

0

Visual Studio 2015 Community Version - #include <stdio.h> nicht gefunden - Library installieren?

Hallo, ich möchte in Visual Studio 2015 Community Editon in C programieren. Dafür habe ich eine *.cpp Datei erstellt und die Endung auf *.c geändert. Nun möchte ich eine simple Textausgabe erzeugen. Mein Quellcode ist wie folgt:

include <stdio.h>

void main() {

printf("\nDies ist ein erstes C-Programm. \n"); }

Es werden allerdings folgende Fehlermeldung angezeigt:

  • Die Datei "Quelle" kann nicht geöffnet werden: "stdio.h".
  • Fehler C1083 Datei (Include) kann nicht geöffnet werden: "stdio.h":

Vermutung: die benötigte Library stdio.h ist nicht auf dem Computer hinterlegt.

Vielen Dank im Voraus.

...zur Frage

C++ Die Datei "Quelle" kann nicht geöffnet werden string.h?

ich habe visual Studio nun 3 mal deinstalliert und installiert. anfangs vor ein paar Wochen ging es noch, und jetzt geht es einfach nicht mehr. er findet die Bibliothek nicht? was kann ich tun? ich brauche die Funktion strcopy die nur über #include <string.h> aufrufbar ist..

...zur Frage

Warum funktioniert der include(ein ordner zurück) befehl nicht?

Quellcode: https://pastebin.com/VKHXBvht

Fehler: Warning: include(../db.php): failed to open stream: No such file or directory in /mnt/web416/c0/01/59361801/htdocs/webseite/header/header_loging.php on line 3 Warning: include(): Failed opening '../db.php' for inclusion (include_path='.:/opt/RZphp72/includes') in /mnt/web416/c0/01/59361801/htdocs/webseite/header/header_loging.php on line 3 Warning: include(../login.php): failed to open stream: No such file or directory in /mnt/web416/c0/01/59361801/htdocs/webseite/header/header_loging.php on line 4 Warning: include(): Failed opening '../login.php' for inclusion (include_path='.:/opt/RZphp72/includes') in /mnt/web416/c0/01/59361801/htdocs/webseite/header/header_loging.php on line 4 Notice: Undefined variable: kunden_id in /mnt/web416/c0/01/59361801/htdocs/webseite/header/header_loging.php on line 5 Warning: mysqli::query(): Couldn't fetch mysqli in /mnt/web416/c0/01/59361801/htdocs/webseite/header/header_loging.php on line 5

...zur Frage

Kann keine wir rar datei mehr entpacken

Ich habe seit neustem ein Problem beim entpacken von .rar dateien. Und zwar schreibt mir das Programm winrar 5.11 beim entpacken folgendes:

ein beschädigter header wurde gefunden

Wie kann ich diesen Fehler beheben oder was kann ich generell machen? Ich wäre über eure hilfe dankbar.

Gruß

...zur Frage

c++ compiler "nicht definierter verweis auf ..."

hallo

ich habe eine .hpp mit der struktur einer klasse und eine passende .cpp in welcher die methoden deklariert werden. füge ich nun die .hpp per include in meine main.cpp ein findet es zwar die klasse und ich kann sie normal verwenden jedoch kommt beim Kompilieren dann die meldung "nicht definierter verweis auf ..(alle verwendeten methoden der klasse)" woran könnte das liegen ? die klassen.cpp verweist natürlich auch auf die dementsprechende headerdatei und die .o datei besteht auch zur passenden klassen.cpp

lösungsvorschläge?

zur vorbeugung "unnötiger" fragen eingebunden per #include "...hpp" in der .hpp steht auch das #ifndef HPP, #define HPP, #endif ,welches mit dem problem aber ja nichts zu tun hat

...zur Frage

Was möchtest Du wissen?