Hallo zusammen,
momentan programmiere ich an einem für meine Verhältnisse großen
C-Projekt. Als IDE nutzte ich LPC Xpresso und als Hardware den
LPC1769...
Jetzt habe ich mehrere .h- Dateien und .c- Dateien. In einer dieser
Headerdateien möchte ich eine Funktion deklarieren die einen Datentyp
nutzt der in einer anderen Headerdatei definiert ist.
Das bedeutet, dass ich in der Headerdatei in der ich meine Funktion
deklariere die Headerdatei in der der Dateityp definiert ist einbinde.
Jetzt meine Frage: Ist das ein schöner Programmierstil oder gibt es
bessere Lösungen?
Schöne Grüße und Danke für euere Antworten!
Einzige Alternative dazu wäre es, projektweit nur eine einzige
Headerdatei für alle Deklarationen zu nehmen. Ob das nun schöner
ist, ist Geschmacksfrage.
Je nachdem, um was für einen Typ es sich handelt und wie er benutzt
wird, kann man solche Abhängigkeiten u. U. auch vermeiden. Wenn
deine Funktion beispielsweise nur einen Zeiger auf eine Struktur
"struct foo" übergeben bekommt, die Struktur selbst aber woanders
definiert wird, dann kannst du auch über der Funktion drüber schreiben:
1
structfoo;
2
3
voidprocess_foo(structfoo*);
Eine solcherart anonyme Struktur ist dem Compiler damit syntaktisch
bekannt, und er kann davon einen Zeiger bilden (ihn aber noch nicht
dereferenzieren).
Owbow123 schrieb:> Das bedeutet, dass ich in der Headerdatei in der ich meine Funktion> deklariere die Headerdatei in der der Dateityp definiert ist einbinde.
Das ist der einzig sinnvolle Weg (auch wenn es gelegentlich gegenteilige
Meinungen gibt).
Nur bei wirklich guten Gründen sollte man davon abweichen:
Überall, wo aus einer anderen Headerdatei etwas direkt sichtbar
verwendet wird, gehört das entsprechende #include rein.
Wenn du also in b.h etwas aus a.h verwendest, dann ist ein #include
"a.h" fällig.
Alles klar, ich danke euch allen!
Mich persönlich hat das irgendwie irritiert. Aber wenn das die schönste
Lösung ist dann kann ich ja in Ruhe weiterarbeiten ;-)
Danke!
Gewöhne Dir an, einen Schutz gegen mehrfach Deklarationen einzubauen,
weil der Preprozessor sonst meckert, wenn Macros doppelt ein zweites Mal
deklariert werden.
Im Header File "com.h" schreibt man an Beginn und Ende
#ifndef COM_H
#define COM_H
...
#endif
Wenn com.h dann ein zweites Mal eingebunden wird, dann findet der
Preprozessor COM_H schon definiert und überspringt den Inhalt des
Header-Files.
Suche in Google nach "nested header files"
Wenn man das macht, kann man die Headerfiles beliebig verschachteln.
Klaus Wachtler schrieb:> Wenn du also in b.h etwas aus a.h verwendest, dann ist ein #include> "a.h" fällig.
Ich würd's so wie Jörg sehen. Wenn möglich eine Forward-Deklaration, und
nur, wenn die nicht reicht, ein #include "a.h".
Motto: So wenig wie möglich, so viel wie nötig, damit ein
1
#include"b.h"
als erste Zeile einer Datei nicht zu einem Fehler führt.
Klaus Falser schrieb:> Im Header File "com.h" schreibt man an Beginn und Ende>> #ifndef COM_H>> #define COM_H> ...>> #endif
Ja, bitte so machen! Es gibt compiler die sich sonst beim mehrfachem
aufrufen der deklaration beschweren. (zurecht) :-)
Rolf Magnus schrieb:> Motto: So wenig wie möglich, so viel wie nötig, damit ein #include "b.h"> als erste Zeile einer Datei nicht zu einem Fehler führt.
Warum sollte das zu einem Fehler führen?
Eben das vermeidet man, wenn man "meiner" Regel folgt.
Du hast insofern recht, daß man manchmal mit einem #include weniger
auskommt durch die forward-Deklaration.
Aber außer ein paar msec beim Kompilieren wird man wenig gewinnen.
Klaus Wachtler schrieb:> Aber außer ein paar msec beim Kompilieren wird man wenig gewinnen.
Ist zwar fuer µC-Bastler schwer vorstellbar, aber es gibt auch groessere
Projekte, wo das durchaus eine Rolle spielt. Und allgemein gilt: Um so
geringer die Abhaengigkeiten, um so kleiner die Sorgen!
Marwin schrieb:> Ist zwar fuer µC-Bastler schwer vorstellbar, aber es gibt auch groessere> Projekte, wo das durchaus eine Rolle spielt. Und allgemein gilt: Um so> geringer die Abhaengigkeiten, um so kleiner die Sorgen!
und was ist wenn in der header datei aus einer Struct auf einmal eine
Class wird?
Ich finde es auch sehr unschön selber eine forward-Deklaration zu
schreiben, da binde ich lieber die header datei ein.
Peter II schrieb:> Marwin schrieb:>> Ist zwar fuer µC-Bastler schwer vorstellbar, aber es gibt auch groessere>> Projekte, wo das durchaus eine Rolle spielt. Und allgemein gilt: Um so>> geringer die Abhaengigkeiten, um so kleiner die Sorgen!>> und was ist wenn in der header datei aus einer Struct auf einmal eine> Class wird?
Spätestens beim Compilieren des cpp Files kommt der Compiler dann drauf.
Denn dort führt dann kein Weg mehr am #include vorbei (und natürlich
auch immer den eigenen Header inkludieren)
>> Ich finde es auch sehr unschön selber eine forward-Deklaration zu> schreiben, da binde ich lieber die header datei ein.
Tu ich auch.
Allerdings landet man dann oft in der Situation (gerade bei größeren
Projekten), dass man damit 'make' im Grunde ad absurdum führt. Über die
Include Files hängt dann alles indirekt mit allem anderen zusammen.
Und manchmal gehts auch gar nicht anders als mit einer Forward
Deklaration. Da ist es dann schon gut, wenn man weiß das es sowas gibt.
Forward-Deklarationen für Strukturen und Klassen verwendet man oft bei
größeren Bibliotheken. Es gibt dort zwei Gruppen von Header-Files:
Die eine (i.Allg. kleinere Gruppe) ist für den Anwender der Bibliothek
bestimmt und enthält nur die Beschreibung des API. Strukturen und Klas-
sen, von denen der Anwender nur Pointer oder Referenzen benutzt und
deren Inhalt für ihn uninteressant sind (also bspw. Handles), sind nur
forward-deklariert. Das hat drei Vorteile:
1. Es hält den Anwender davon ab, auf Elemente dieser Strukturen zuzu-
greifen, die in der nächsten Version der Bibliothek vielleicht nicht
mehr vorhanden sind.
2. Es wird dadurch vermieden, dass ein ganzer Rattenschwanz weiterer
Headers inkludiert werden muss, weil die Struktur weitere Strukturen
als Elemente enthält.
3. Wird die Bibliothek close-source ausgeliefert, erhält der Anwender
nur die Header-Files dieser ersten Gruppe und bekommt dadurch keine
Implementierungsdetails interner Datenstrukturen zu Gesicht.
Die zweite Gruppe von Header-Files wird (zusammen mit der ersten) nur
zum Bauen der Bibliothek benötigt. Sie enthält die vollständigen Dekla-
rationen aller Strukturen und Klassen.
Peter II schrieb:> und was ist wenn in der header datei aus einer Struct auf einmal eine> Class wird?
Gar nichts. Der Zeiger darauf ist nämlich immer noch genauso groß,
und mehr kann man mit der Forward-Deklaration sowieso nicht
anfangen. ;-)
Jörg Wunsch schrieb:> Peter II schrieb:>> und was ist wenn in der header datei aus einer Struct auf einmal eine>> Class wird?>> Gar nichts. Der Zeiger darauf ist nämlich immer noch genauso groß,> und mehr kann man mit der Forward-Deklaration sowieso nicht> anfangen. ;-)
Einfacher: Eine struct ist eine Klasse in C++. Damit ändert sich gar
nichts.
Sascha schrieb:> Jörg Wunsch schrieb:>> Peter II schrieb:>>> und was ist wenn in der header datei aus einer Struct auf einmal eine>>> Class wird?>>>> Gar nichts. Der Zeiger darauf ist nämlich immer noch genauso groß,>> und mehr kann man mit der Forward-Deklaration sowieso nicht>> anfangen. ;-)>> Einfacher: Eine struct ist eine Klasse in C++. Damit ändert sich gar> nichts.
Ein Compiler wird das trotzdem anmeckern. Sonst wäre es ein bischen zu
einfach, Zugriff auf private und pretected Member zu bekommen, indem man
einfach eine Klasse zu einer struct 'degradiert'.
Schon klar. Man kann diesen Zugriff immer erzwingen wenn man unbedingt
möchte, aber nur durch Umnennen von class zu struct ... das wäre ein
bischen zu banal. Daher wird der Compiler das Anmeckern, wenn man ihm
ein X für ein U vormachen will und er das mitkriegt. Und spätestens wenn
dann in der Implementierung die Stunde der Wahrheit schlägt und eine
Forward Deklaration nicht mehr reicht, wird er es mitkriegen.
Full ack: Yalu X.
Man sollte allerdings in den API Header Dateien darauf achten das diese
in jeder (Position) in eine andere Datei Eingebungen werden kann. Nichts
ist schlimmer als heraussuchen zu müssen, welche Datei man vorher
einbinden muss.
Also wenn in einer API Header Datei z.B. uint8_t genutzt wird sollte man
auch in dieser ein #include "stdint.h" einfügen.
Zum testen Compiliert man ein c file mit nur dieser Header Datei. Kommt
da keine Warnung oder Fehler, ist man auf dem richtigen weg :)
Karl Heinz Buchegger schrieb:> Ein Compiler wird das trotzdem anmeckern. Sonst wäre es ein bischen zu> einfach, Zugriff auf private und pretected Member zu bekommen, indem man> einfach eine Klasse zu einer struct 'degradiert'.
Was soll der meckern?
Wenn ich aus
struct foo
{
int a;
}
ein
class foo
{
public:
int a;
}
mache, dann hat er eigentlich gar nichts zu meckern.
Man kann auch einfach das "struct" lassen. Was heißt schon "eine Klasse
daraus machen"? WOhl, daß man nun auch ableiten will. Oder Methoden
dazu.
Also:
struct bar : foo
{
int Do(void);
}
Abgesehen davon ist das alles natürlich eh Pippifax-Refactoring, auch
wenn man unbedingt struct zu class machen will und dann halt hier und da
ein "public" einstreuen muß.
Sascha schrieb:> Wenn ich aus>> struct foo> {> int a;> }>> ein>> class foo> {> public:> int a;> }>> mache, dann hat er eigentlich gar nichts zu meckern.
Wenn ich aus
class foo
{
private:
int a;
};
ein
struct foo
{
int a;
}
foo b;
b.a = 5;
mache, dann ist das nicht wirklich im Sinne des Erfinders.
Wenn dem Compiler präsentiert wird:
struct foo;
class foo
{
private:
int a;
};
was gilt denn dann? Die struct oder die class? private oder public?
> Man kann auch einfach das "struct" lassen. Was heißt schon "eine Klasse> daraus machen"? WOhl, daß man nun auch ableiten will. Oder Methoden> dazu.
Access Qualifiers!
Das ist der einzige Unterschied zwischen class und struct. Und damit die
konsistent sind, sollte man seinen Compiler nicht anlügen.
Karl Heinz Buchegger schrieb:> Wenn ich aus>> class foo> {> private:> int a;> };>>> ein>> struct foo> {> int a;> }
Das ist ja auch Käse, und stand nie zur Debatte.
Ich habe doch hingeschrieben, wie es realistisch ist: von struct zu
class mit public:.
Sascha schrieb:> Karl Heinz Buchegger schrieb:>> Wenn ich aus>>>> class foo>> {>> private:>> int a;>> };>>>>>> ein>>>> struct foo>> {>> int a;>> }>> Das ist ja auch Käse, und stand nie zur Debatte.
Ich würde sagen: Doch. Genau darum drehte sich die Debatte.
> Ich habe doch hingeschrieben, wie es realistisch ist: von struct zu> class mit public:.
Das ist nicht wirklich realistisch. Wenn ich eine struct zu einer class
mache und sonst nichts verändere, dann kann ich mir den Vorgang auch
gleich sparen. Denn das bringt mir nichts. Ändere ich sowas um, dann
erwarte ich mir ja einen Vorteil davon.
Karl Heinz Buchegger schrieb:>> Das ist ja auch Käse, und stand nie zur Debatte.>> Ich würde sagen: Doch. Genau darum drehte sich die Debatte.
Jetzt bin ich geneigt, einzuwerfen, daß die ursprüngliche Debatte darum
ging, ob man eine .h includen soll, wenn man etwas aus ihr verwendet :-)
Nicht, daß ich erwarten würde, das interessiert jemanden...
Trotzdem viel Spaß!
Karl Heinz Buchegger schrieb:>> Das ist ja auch Käse, und stand nie zur Debatte.>> Ich würde sagen: Doch. Genau darum drehte sich die Debatte.
Nein. Wo schrieb jemand, er wolle eine Klasse zu einer Struktur machen?
Das Gegenteil war der Fall!
>> Ich habe doch hingeschrieben, wie es realistisch ist: von struct zu>> class mit public:.>> Das ist nicht wirklich realistisch. Wenn ich eine struct zu einer class> mache und sonst nichts verändere, dann kann ich mir den Vorgang auch> gleich sparen. Denn das bringt mir nichts. Ändere ich sowas um, dann> erwarte ich mir ja einen Vorteil davon.
Liest du meine Beispiele eigentlich? Ich habe extra eine Vererbung und
eine Methode eingefügt.
Warum sonst sollte man eine Struktur zu einer Klasse machen wollen
(insbesondere, wenn man wie fast jeder die Äquivalenz beider nicht
kennt)?
Sascha schrieb:> Karl Heinz Buchegger schrieb:>>> Das ist ja auch Käse, und stand nie zur Debatte.>>>> Ich würde sagen: Doch. Genau darum drehte sich die Debatte.>> Nein. Wo schrieb jemand, er wolle eine Klasse zu einer Struktur machen?> Das Gegenteil war der Fall!
Das Thema kam als Nebenschauplatz auf, als die Frage auftauchte was wohl
passieren würde, wenn man einen Forward Deklaration einer Klasse
fälschlicherweise als struct ausgeben würde.
> Liest du meine Beispiele eigentlich? Ich habe extra eine Vererbung und> eine Methode eingefügt.
Und, was besagt das?
>> Warum sonst sollte man eine Struktur zu einer Klasse machen wollen> (insbesondere, wenn man wie fast jeder die Äquivalenz beider nicht> kennt)?
Autsch.
class und struct sind in ALLEN Punkten identisch! Mit zwei Ausnahmen:
der Default Access Qualifier bei einer struct ist public, während er bei
einer class private ist. Bei einer Vererbung einer struct ist der
Default ebenfalls public, während er bei einer class private ist.
Soviel zum Thema: Du weisst was die Unterschiede von struct und class
sind.
Eine struct kann Member haben
Eine struct kann Konstructor/Destructor haben
Eine struct kann Operatoren haben
Von einer struct kann man ableiten.
...
Man kann mit einer struct alles machen, was man mit einer class auch
machen kann.
Einziger Unterschied:
struct a
{
int m;
};
ist gleichwertig zu
struct a
{
public:
int m;
};
während
class a
{
int m;
};
gleichwertig zu
class a
{
private:
int m;
}
ist.
Nimmt man eine struct her und tauscht das struct gegen class aus und
schreibt noch ein 'public über alles' rein, dann hat man NICHTS
gewonnen. Gar nichts. Niente, Nothing, Nada. Warum sollte man daher
diese Veränderung machen, wenns eh nichts bringt?
Karl Heinz Buchegger schrieb:> Soviel zum Thema: Du weisst was die Unterschiede von struct und class> sind.
Ich weiß das alles. Ich kenne die Norm übrigens.
Warum wirst du so herablassend, wo ich in mehreren Postings nun gezeigt
habe, daß ich die Details genau weiß?
Schlechten Tag gehabt?
Sascha schrieb:> Karl Heinz Buchegger schrieb:>> Soviel zum Thema: Du weisst was die Unterschiede von struct und class>> sind.>> Ich weiß das alles. Ich kenne die Norm übrigens.>> Warum wirst du so herablassend, wo ich in mehreren Postings nun gezeigt> habe, daß ich die Details genau weiß?
Warum beziehst du dich dann in deinem Beispiel darauf, dass zb
Memberfunktionen wichtig wären?
Es ist völlig irrelevant.
Wenn du die Aussage triffst, eine struct würde man zu einer class
umbauen müssen, weil
> > Liest du meine Beispiele eigentlich? Ich habe extra eine Vererbung und> eine Methode eingefügt.
dann ist das ganz einfach falsch,
niemals durchgehen lassen. Darum geht es. Und nur darum.
Selbst dann, wenn sich der Sacherverhalt so ergibt
a.h.
1
classa
2
{
3
};
b.h
1
structa;
2
3
classb
4
{
5
voidfoo(a*myA);
6
};
1
#include"a.h"
2
#include"b.h"
3
4
voidb::foo(a*myA)
5
{
6
...
7
}
Da ist eine Diagnostik fällig.
Irgendwas in der Richtung: "a, which is of class type, was first
introduced as struct"
Womit wir wieder zurück bei der Zusatzfrage wären, die zwischendurch
aufgetaucht ist
Beitrag "Re: Programmierstil"
Karl Heinz Buchegger schrieb:> Warum beziehst du dich dann in deinem Beispiel darauf, dass zb> Memberfunktionen wichtig wären?> Es ist völlig irrelevant.seufz> Wenn du die Aussage triffst, eine struct würde man zu einer class> umbauen müssen, weil>> > Liest du meine Beispiele eigentlich? Ich habe extra eine Vererbung und>> eine Methode eingefügt.> dann ist das ganz einfach falsch,
Weil ich den "allgemeinen Wissensstand" annehme. Was heißt denn sonst,
er wolle eine struct zu einer Klasse umbauen?
Was wird er wohl meinen?
Aber rutsch mir doch den Buckel runter. Du willst heute doch eh nur
flamen.
>> niemals durchgehen lassen.
Sei mit solchen Aussagen mal vorsichtig. ;-)
1
$ cat foo.C
2
class a
3
{
4
};
5
6
void foo( struct a bar )
7
{
8
}
9
$ gcc -Wall -Wextra -c foo.C
10
foo.C:5: warning: unused parameter ‘bar’
Er lässt es also sehr wohl durchgehen ...
Aber:
1
$ cat bar.C
2
class a
3
{
4
int priv_member;
5
};
6
7
int foo( struct a bar )
8
{
9
return bar.priv_member;
10
}
11
$ gcc -Wall -Wextra -c bar.C
12
bar.C: In function ‘int foo(a)’:
13
bar.C:3: error: ‘int a::priv_member’ is private
14
bar.C:8: error: within this context
Er kennt also sehr wohl seine Verpflichtungen, wenn man eine Klasse
nun Struktur nennt. Die beiden Schlüsselwörter sind für ihn wirklich
"im laufenden Betrieb" austauschbar; entscheidend für den Status, ob
ein Mitglied nun initial als private oder public angenommen wird, ist
lediglich die Definition, und die gibt's ohnehin nur einmal.
Marwin schrieb:> Ist zwar fuer µC-Bastler schwer vorstellbar, aber es gibt auch groessere> Projekte, wo das durchaus eine Rolle spielt. Und allgemein gilt: Um so> geringer die Abhaengigkeiten, um so kleiner die Sorgen!
Gestern noch sprach ich davon, heute sitze ich deswegen in der Tinte - 2
Frameworks in einem Projekt, beide benutzen den gleichen Namen fuer
einen Typ / ein Define intern. Und natuerlich bleibt das nicht intern,
weil wie wild Alles included wird, was bei 3 nicht auf den Baeumen ist.
Was viel wichtiger ist als irgendwelche Header ist dass das Programm
sinnvoll strukturiert, und jedes Teil unabhängig von den anderen zu
verstehen ist. Diese Teile sollten dann idealerweise nicht größer als
eine Bildschirmseite sein. (25-50 Zeilen)
Eventuell kann es sinnvoll sein ein Betriebssystem zu verwenden. Es gibt
ja da viele. Das ermöglicht Dir es dann das Programm in viele
unabhängige Prozesse zu unterteilen die dann über definierte Kanäle
miteinander sprechen. Das kann das Programm deutlich klarer werden
lassen.
Klaus Wachtler schrieb:> Habe eben mal probiert: template< struct T > T MAX( T a, T b )... geht> nicht, da will er class oder typename haben statt struct :-)
Das muß auch so. ;-)
Das hat allerdings nicht direkt mit Klassen zu tun, sondern nur mit
einer alternativen Bedeutung des Schlüsselworts class. T kann ja auch
int sein. class ist da eher aus historischen Gründen erlaubt, weil es
anfangs das Schlüsselwort typename noch nicht gab. Die
Sprachentwickler zieren sich immer so, wenn es um die Einführung neuer
Schlüsselwörter geht. Deshalb hat static auch so viele verschiedene
Bedeutungen.
Ich hätte auch nicht erwartet, daß es geht - aber
Jörg Wunsch schrieb:> Die beiden Schlüsselwörter sind für ihn wirklich> "im laufenden Betrieb" austauschbar
hatte mich doch etwas neugierig gemacht.
Klaus Wachtler schrieb:> Jörg Wunsch schrieb:>> Die beiden Schlüsselwörter sind für ihn wirklich>> "im laufenden Betrieb" austauschbar>> hatte mich doch etwas neugierig gemacht.
Dann schränke ich das mal ein: nicht syntaktisch austauschbar,
aber semantisch schon. Sie legen halt nur die Sichtbarkeit der
Mitglieder fest, für die in der Definition keine explizite
Festlegung getroffen worden ist, aber da es nur genau eine
Definition in einer Übersetzungseinheit geben kann, ist das
konkret benutzte Schlüsselwort ("struct" oder "class") nur an
dieser Stelle entscheidend; an allen anderen Stellen, an denen
auf diese Struktur/Klasse verwiesen wird, werden beide gleich
behandelt.
Die template-Geschichte ist dagegen ein völlig anderer Kontext.
Klaus Wachtler schrieb:> Tja, in C++ könnte man namespaces nehmen. Macht nur keiner, das wäre zu> leicht.
Einmal das, aber bei einem Konflikt mit einem Define hilft das nichts.
Bei namespaces kann es uebrigens auch noch zu anderen drolligen
Konflikten kommen...