Ähnliche Frage: Include von Konstanten.



  • Hallo

    leider noch eine ähnliche Frage.
    Situation: zwei Dateipaare i1.cpp, i1.h bzw. i2.cpp, i2.h

    i1.h benötigt eine int Konstante aus i2.h und i2.h benötigt eine
    int Konstante aus i2.h
    Prototypen geht hier (im Ggs. zu Klassen) ja nicht, da dadurch die Konstante erneut deklariert würde.

    #include "i1.h" in i2.h und #include "i2.h" in i1.h zu schreiben, geht
    auch nicht, da dadurch eine Endlosschleife von #include-Vorgängen
    entsteht. Geht selbst dann nicht, wenn man den üblichen Trick
    #if !defined I1
    #include "i1.h"
    #define I1
    #endif
    anwendet.

    Oder allgemeiner: gibt es ein allgemeines Rezept, das nicht nur mit
    2, sondern auch 3,4 oder 10 Dateien funktioniert, die sich wechselseitig
    includen müßten ??

    Oder muß man dann für jedes Tupel von Dateien einen "Ober-Header"
    i12.h schreiben, der dann i1.h und i2.h includet und entsprechend
    i12.cpp, der nur i12.h includet, nicht aber i1.h und i2.h separat ?
    Elegant ist das ja nicht gerade. Wieso ist das in C++ so kompliziert, in
    keiner anderen Sprache, die ich kenne, aber auch nur das geringste Problem ?

    ??



  • Hobbyprogrammierer schrieb:

    Prototypen geht hier (im Ggs. zu Klassen) ja nicht, da dadurch die Konstante erneut deklariert würde.

    Sicher geht das, nur ist das, was du für eine Konstantendeklaration hälst, eine Definition. Eine Deklaration sieht so aus:

    extern const int foo;
    


  • Gut, das geht. Heißt das nun aber, daß ich,
    wenn ich i1.h in i2.h brauche und i2.h in i1.h brauche,
    *jede* einzelne Konstante, Funktion und Klasse nochmal extra
    als Prototypen einbinden muß ? Abgesehen davon, daß das doch
    recht mühsam sein kann, wenn man 1000 Klassen hat, von denen stets
    mehrere voneinander abhängen können.

    Gibt es nicht in C++ eine Möglichkeit, so zu #includen, daß jeder gefragte Header einmal und nur einmal eingebunden wird, sodaß Endlosschleifen beim #includen einfach verhindert werden ? Führt der Compiler nicht Buch, welche Dateien er schon
    includet hat, sodaß er #include-Schleifen bemerkt, bevor er ein- und
    dassselbe File 256-mal eingebunden hat und/oder die Verschachtelungsgrenze
    erreicht?



  • Nein, der Compiler bemerkt das überhaupt nicht - der bekommt nur ein einzelnes File vorgesetzt (nachdem der Präprozessor alle #include's auseinandergenommen hat). Aber du kannst den Präprozessor dazu bringen, jeden Header nur einmal einzubinden - Stichwort "Include Guard".

    (für deine Anwendung würde es sich anbieten, alle gemeinsamen Deklarationen in einen unabhängigen Header zu packen - dann brauchst du in jeder Datei nur ein '#include "defs.h"' am Anfang)



  • Hallo!

    Danke für die Antworten allseits. Mir ist noch eine Lösung eingefallen, die
    zwar nicht so elegant ist, aber nach meinen Versuchen funktioniert:

    Jede Datei incdatei.h, die irgendwann includet werden soll, umrahmen mit:

    #if !defined INCDATEIH
    #define INCDATEIH

    ... hier der ganze Rest der Datei incdatei.h .....

    #endif

    d.h. das #endif an's Dateiende schreiben. #include "incdatei.h" geht selbst
    dann noch, wenn in incdatei.h wiederum #include "incdatei.h" steht, ohne
    Endlosverschachtelung von #includes zu hervorrufen (da
    ja INCDATEIH dann bereits definiert ist).

    Grüße



  • Genau das ist der "Include Guard" den CStoll erwähnt hat.



  • Und genau das wird nicht benötigt.



  • Ach. Meinst du hiefür nicht oder allgemein nicht?
    Ich finds eigentlich recht hilfreich.



  • Generell klingt das ein bißchen nach nem Design-Problem. Solche Zyklischen Abhängigkeiten sollte man schnellstmöglich loswerden.



  • Braunstein schrieb:

    Ach. Meinst du hiefür nicht oder allgemein nicht?
    Ich finds eigentlich recht hilfreich.

    Naja, welche Datei wolltest Du denn gerne zuerst anschaun, wenn Du für Datei1 Datei2 brauchst und für Datei2 die Datei1. Das ist ein generelles Problem, das sich auch mit Include-Guards nicht besteitigen läßt.



  • Ein "GlobalDefs.h" machen und alle Konstanten da rein packen?

    edit: Sagte CStoll schon



  • @Jester
    Ich hab das mit den Include-Guards jetzt nicht auf zyklische Abhängigkeiten bezogen. Probleme die daraus enstehen sollte man anders lösen. Ich find sie allgemein recht hilfreich.
    Zyklische Abhängigkeiten sind ja meistens ein Design-Problem. Wenn es sich nicht mit Forwarddeklarationen lösen lässt, hat man irgendwas falsch gemacht.



  • Klar sollte man Include Guards benutzen, nur passen sie nicht auf die Problemstellung.



  • Hallo,

    also zyklische Abhängigkeiten sind meines Erachtens kein prinzipieller Design-Fehler, im Gegenteil.
    Zyklische Includes entstehen z.B. dadurch, daß Klasse A Bezeichner von Klasse
    B benötigt. z.B. in der Form
    void A::init(B aB, c aC) {
    ... B.getItem(...)
    }

    und Objekte vom Typ B ihrerseits Objekte vom Typ A übergeben werden und Methoden von A aufrufen.
    Baut man dann jede Klasse in eine eigene Quelldatei, sind zyklische
    Includes unvermeidlich.

    Und genau so soll doch OO-Design der "reinen OO-Lehre" nach sein: Eine Menge
    von Objekten, die vermittels gegenseitiger Methodenaufrufe miteinander kommunizieren.

    Grüße.



  • Hobbyprogrammierer schrieb:

    Zyklische Includes entstehen z.B. dadurch, daß Klasse A Bezeichner von Klasse
    B benötigt. z.B. in der Form
    void A::init(B aB, c aC) {
    ... B.getItem(...)
    }

    und Objekte vom Typ B ihrerseits Objekte vom Typ A übergeben werden und Methoden von A aufrufen.

    Dann nimmt man Referenzen als Parameter, wies sich gehört, und die Definitionsabhängigkeit ist geregelt.

    Baut man dann jede Klasse in eine eigene Quelldatei, sind zyklische
    Includes unvermeidlich.

    Nö, es werden ja nur die Header inkludiert.

    Und genau so soll doch OO-Design der "reinen OO-Lehre" nach sein: Eine Menge
    von Objekten, die vermittels gegenseitiger Methodenaufrufe miteinander kommunizieren.

    Das hat aber nichts mit zyklischen Includes zu tun.



  • Bloß weil sie miteinander Kommunizieren müssen sie doch nicht gleich zyklisch abhängig sein. Abhängigkeiten sind generell ne unschöne Sache, zyklische sind fast immer Design-Fehler. Oft kann man durch Einführung einer Basisklasse solche Abhängigkeiten loswerden.

    Dein Beispiel zeigt, daß es möglich ist zyklische Abhängigkeiten einzubauen. Kannst Du auch eines geben, das zeigt, daß es nötig ist? Wenn zwei Dinge so intensiv gekoppelt sind, daß sie gegenseitig abhängig sind, inwiefern macht es dann überhaupt Sinn sie auf diese Weise in zwei Teile zu zerlegen?



  • Hallo

    Beispiel gerne:
    Präzise Simulation elektronischer Schaltungen. Klasseneinteilung "Transistor", "Widerstand", "Kondensator","Leiterbahn" usw... jedes Bauteil X führt eine Liste
    aller Bauteilen in der Umgebung und ihrer Abstände zum Bauteil X.

    Transistor T1 liege örtlich dicht neben Widerstand R1.
    Dann benötigt zur genauen Simulation T1 sowohl Wert als auch Temperatur
    von R1 sowie den Abstand T1---R1, denn über die Luft im Zwischenraum manipuliert
    die Temperatur von R1 den Arbeitspunkt von T1. Ebenso braucht R1 Daten von
    T1, da sich das Widerstandsrauschen mit der Umgebunstemperatur ändert.

    Dasselbse gilt für alle Paarungen Widerstand/Kondensator (Keramikkondensatoren
    in HF-Schaltungen ändern ihre Kapazität mit der Umgebungswärme, also die
    Frequenz eines HF-Schwingkreises), Transistor/Kondensator und Transistor/Transistor, usw.

    In allen diesen Fällen braucht transistor.h Attribute von widerstand.h und
    auch umgekehrt.

    Grüße

    PS. wenn man Bücher über OOP mit anderen Sprachen als C++ liest, dann
    wird man feststellen, daß die wechselseitige Beeinflussung von Objekten
    nicht nur unerwünscht, sondern sogar Design-Prinzip der OO ist. In Smalltalk
    würde man niemals zwei Klassen zu einer vereinigen, nur weil die beiden
    über messages eng gekoppelt sind.



  • Hobbyprogrammierer schrieb:

    Beispiel gerne:
    Präzise Simulation elektronischer Schaltungen. Klasseneinteilung "Transistor", "Widerstand", "Kondensator","Leiterbahn" usw... jedes Bauteil X führt eine Liste
    aller Bauteilen in der Umgebung und ihrer Abstände zum Bauteil X.

    Da hast Du auch gleich schon ein super Stichwort mitgeliefert: Bauteil!
    Wir bauen eine abstrakte Basisklasse Bauteil, die alle nötigen Infos zur Verfügung stellen kann. Alle konkreten Bauteile erben davon und führen eine Liste benachbarter Bauteile. Ein wunderschönes Beispiel, wie man entkoppeln kann. Insbesondere ist es nun einfach neue Bauteile hinzuzunehmen.

    Den Rest, daß zyklische Abhängigkeiten erwünscht seien kommentiere ich einfach nicht. Da ist mir ehrlich gesagt meine Zeit zu schade.



  • Ich bin in Elektronik nicht so bewandert, aber müssen die Bauteile wirklich so viel voneinander wissen, oder reichts, wenn die Schaltung die Infos hat?



  • Hallo Jester,

    aber eine abstrakte Basisklasse "Bauteil" zu konstruieren (das geht natürlich),
    die alle Eigenschaften der realen Bauteile besitzt, geht nicht.
    Ein Transistor braucht zum Beispiel Vierpol-Parameter, Kniespannung, PN-Übergangskapazitäten, die beim Widerstand sinnlos sind. Ein Kondensator
    muß modelliert werden mit Eigenschaften wie Leckstrom, den gibt's aber bei der
    Diode nicht und beim Transistor auch nicht.
    Eine Basisklasse "Bauteil" hätte demnach nur einen Bruchteil aller Einflußgrößen
    (etwa Name, Platzierung, Bauform,...) alles Elektrische wäre doch wieder vom
    Bauteiltyp abhängig.

    Michael E:

    Kommt drauf an, wie genau mal simulieren will (oder muß). Für eine
    Blinkerschaltung muß man keine Kondensatortemperaturen modellieren, aber
    für einen HF-Schwingkreis, bei dem 1% Abweichung der Kapazitäten schon
    10 MHz Frequenzabweichung bedeuten, ist sowas notwendig.

    Oder denk mal an integrierte Schaltungen. Da beeinflußt jedes Bauteil
    jedes andere, vor allem nantürlich die in der Umgebung, da ja dort
    die Transistoren direkt in dasselbe Substrat eingelassen sind, in dem
    auch die Widerstandsbahnen liegen. Da sind Wechselwirkungen unabdingbar,
    vor allem wieder bei HF-Anwendungen.

    Grüße


Anmelden zum Antworten