Gelöst: Organisation von Dateien
-
Hallo liebe Community,
ich bin z.Z. dabei ein paar Funktionen und Strukturen zu schreiben.
Ich möchte alles dabei möglichst nach Sinn und Zweck der Strukturen und Funktionen in unterschiedlichen Dateien sortieren.Ich bin mir nun aber überhaupt nicht sicher, wie ich soetwas am besten organisiere. Also ich habe eine Headerdatei geschrieben, in die ich ein parr #define Sachen reingehauen habe. Weiter hin stehen da die Funktionsdeklarationen drinne. Die Funktionen an sich stehen aber in einer gleichnamigen *.c Dateien, in die ich den Header inkludiere.
Ich gehe davon aus, dass ich irgendwann mal relativ viele Dateien zusammen haben werde, die ich dann anderen Projekten verwenden kann. Dazu währe es mir lieb, wenn ich nicht hunderte Headerdateien einbinden muss und eine elenenlange Makefile schreiben muss.Also kann mir jmd. sagen wie man solche großen Ansammlungen Organisiert , oder gibt es dazu einen schönen Hinweis im Internet? Bis jetzt konnte ich da noch nichts finden und ich denke, dass solche Dinge sowas wie die Softskilles für Programmierer sind,...also Organisation des eigen Codes.
danke für die Hilfe
mfg martin
-
WC-Stein schrieb:
Ich gehe davon aus, dass ich irgendwann mal relativ viele Dateien zusammen haben werde, die ich dann anderen Projekten verwenden kann. Dazu währe es mir lieb, wenn ich nicht hunderte Headerdateien einbinden muss und eine elenenlange Makefile schreiben muss.
du schreibst also anscheinend eine Bibliothek. Du kannst es als shared library kompilieren (.so unter Unix, .dll unter Windows) und andere Programme linken nur gegen die libraries.
-
Hallo,
danke für den Tipp.Wie mache ich das genau?
Wie compiliere ich sie als *.so
Wie binde ich diese dynamische Bibliothek dann ein?
Gibt es bestimmte Konventionen in einer solchen Bibliothek?lG martin
-
Beim Umgehen mit vielen Dateien (also viele .c und .h Dateien) mache ich das so:
2 Verzeichnisse im Project Verzeichnis: \SOURCE und \HEADER. In SOURCE kommen alle .c Dateien. In HEADER alle .h Dateien. Es gibt ein Master-Header, der systemweite DEFINES und typedefs enthält und alle anderen .h Dateien einbindet. Auerßdem bindet er auch die Header der STandardbibliothek ein, die benötigt werden. Jede Source Datei bindet den Master Header ein und seine eigene Header Datei. Also z.b.PROJECT-VERZEICHNIS Einbindung ------------------- ---------- | defs.h: |---->\SOURCE #include <stdio.h> | | #include "1.h" | |->1.c #include "2.h" | |->2.c #include "3.h" | |->3.c #include ... | |->... 1.c: | #include "defs.h" |---->\HEADER #include "1.h" | 2.c: |->defs.h #include "defs.h" |->1.h #include "2.h" |->2.h usw. |->3.h |->...
Jedes Programm das nun mit diesem Paket arbeiten möchte, bindet nur "defs.h" ein und fertig.
Jede HEader Datei hat noch dies hier drin
#ifndef __NAMEDERHEADERHATEI_H__ #define __NAMEDERHEADERHATEI_H__ // (code) #endif // __NAMEDERHEADERHATEI_H__
EDIT: Mein gewählten Name "defs.h" würde man natürlich durch einen Ausdrucksstarken Namen ersetzen. Z.B. "hashlib.h" oder "conlib.h" usw.
-
Danke das war sehr hilfreich!
Im Grunde mache ich das auch so ähnlich. Ich frage mich bloß, ob man beim einbinden der *.c Dateien das schreiben langer Makefiles umgehen kann. Denn wenn ich jetzt die ganzen Dateien verwenden möchte muss ich ja jede einzelne mit gcc linken, oder nicht?
-
Bin kein gcc Spezi. Ich denke aber du kannst es so machen, daß du es zu ein .o/.so Datei einmal komplett zusammencompilierst. Und dann in dem Anderen Projekt nur defs.h einbindest und er sich die Auflösungen aus der Object-Datei holt.
Frag da aber nochmal nen gcc und makefile spezi. Für mich erledigt das das Visual Studio.
-
Jau, werde ich machen, danke für die Hilfe!
lG Martin
-
Von einer defs.h aus ALLE Header-Dateien zu includen, halte ich für etwas unelegant; es könnte zum Schreiben von schlecht modularisiertem Code verleiten, da man ja überall alles verwenden kann.
Besser ist es, von jeder c-Datei ZUERST die zugehörige Header-Datei includen (um sicherzustellen, dass die keine unbemerkten Abhängigkeiten hat), und dann nur die anderen Header-Dateien, die man gerade braucht. Includes in Header-Dateien selbst sollte man wenn möglich vermeiden, außer wenn es nicht anders geht, für typedefs usw.
Man sollte den Code nicht in zu viele winzige c-Dateien aufspalten, dann bleibt die Anzahl der Dateien auch bei etwas größeren Projekten überschaubar.
Es gehören nur die Funktionsdeklarationen in Headerdateien, die man auch wirklich woanders braucht; die restlichen Funktionen werden nur static in der c-Datei definiert. struct-Definitionen hält man besser auch aus Header-Dateien raus.
Extra eine Bibliothek für den Eigengebrauch zu schreiben ist oft übertrieben. Sinnvoller ist es, die einzenen Quelldateien gut wiederverwendbar zu gestalten. Eine Bibliothek ist nur sinnvoll, wenn sich sehr viel Funktionalität gut vom Rest abgrenzen und hinter einer kleinen Schnittstelle verstecken lässt.
Was die Makefiles betrifft: Man muss nicht für jede Quelldatei eine Regel hinschreiben, es reicht auch etwas allgemeines wie
%.o: %.c $(CC) -c $(CFLAGS) -o $@ $<
und dann zum Linken ein einzelnes
programm: main.o 1.o 2.o 3.o usw.o $(CC) -o $@ $^ $(LDFLAGS)
Für die Abhängigkeiten der Header-Dateien kann man makedepend oder ähnliches verwenden.
Oder man verwendet eine IDE.
-
namespace invader schrieb:
Von einer defs.h aus ALLE Header-Dateien zu includen, halte ich für etwas unelegant; es könnte zum Schreiben von schlecht modularisiertem Code verleiten, da man ja überall alles verwenden kann.
Wenn wir hier über große Systeme reden, kann ich dir hier nicht zustimmen. Jede Header-Datei, die in defs.h eingebunden ist, repräsentiert ein vollständig autonomes Subsystem, das seine Interfacedefinitionen in der zugehörigen Subsystem-Header-Datei implementiert. Die Subsubsysteme werden hier selbstverstädnlich nicht in defs.h eingetragen sondern nur der höchste Abstraktionsgrad des Subsystems. Subsubsystem müssen nicht von außen aufgerufen werden, sondern nur dem übergeordneten Subsystem zur Verfügung stehen um seine Arbeit zu erledigen.
Modulares Programmieren sollte man sowieso stets berücksichtigen und wird durch dieses Design meiner Meinung nach unterstützt, solange man sich daran hält in Header Dateien nur das öffentlich zu machen, was unbedingt öffentlich sein muß um mit dem Subsystem arbeiten zu können.
Es lässt sich auch weiterhin nicht verhindern, daß diese Subsysteme zu einem gewissen Grad sehr miteinander verwickelt sein können. Z.B. in Spielen. Wenn man mal den Weg verfolgt von rohen Daten auf der Platte bis zur Anzeige des Bildes, so wirst du in etwa sowas hier finden.
rohe daten -> filesystem -> memory manager <-> cache manager <-> ressource manager (-> bitmap handler) -> grafics manager -> grafics engine -> open-gl layer -> open-gl -> anzeige
So muß der Cache Manager mit dem Memory Manager zusammenarbeiten. Das Filesystem mit dem Ressouce Manager und evtl Loader-Handlern. Diese Systeme mach ich einfach alle einmal miteinander in defs.h bekannt und dann ist schön. Das ist in meinen Augen elegant.
Besser ist es, von jeder c-Datei ZUERST die zugehörige Header-Datei includen (um sicherzustellen, dass die keine unbemerkten Abhängigkeiten hat), und dann nur die anderen Header-Dateien, die man gerade braucht.
So includet Subsystem selbstverständlich Subsubsystem A Subsubsystem B Subsubsystem C ... also OK.
Includes in Header-Dateien selbst sollte man wenn möglich vermeiden, außer wenn es nicht anders geht, für typedefs usw.
Nein. Ich include lieber einmal gewisse Standardbibliotheken als in 40-50 unterschiedlichen Source-Dateien. Das wäre in meinen Augen unelegant. Außerdem führe ich in defs.h alle Interfacedefinitionen der einzelnen Subsysteme durch Includen der Subsystems-Header zusammen, so daß das ganze System nach außen durch einen einzigen Header dargestellt wird. (hehe das hat was von einem Vergleich: for-schleife <-> rekursive funktion)
Man sollte den Code nicht in zu viele winzige c-Dateien aufspalten, dann bleibt die Anzahl der Dateien auch bei etwas größeren Projekten überschaubar.
Hier sollte der Grad an Abstraktion darüber entscheiden wie dies genau vonstatten geht. Ich denke hier in Schichten. Die unterste Schicht ist nah an der Hardware/Daten etc. Weitere Abstraktionen in höhere Schichten sind nur erlaubt, wenn sie der Sicherheit dienen, Funktionalitäten zusammenfassen oder das Subsystem leichter zugänglich machen.
Es gehören nur die Funktionsdeklarationen in Headerdateien, die man auch wirklich woanders braucht; die restlichen Funktionen werden nur static in der c-Datei definiert. struct-Definitionen hält man besser auch aus Header-Dateien raus.
Jap hier sind wir wieder einer Meinung. Alles was "Privat" sein kann, sollte "Privat" sein. Außer das mit den Structs. Structs sind häufig "Datenübermittler" und somit selber Teil eines Interfaces. Dann können sie selbstverständlich nicht mehr in den .c Dateien versteckt werden, sondern müssen den zusammenarbeitenden Systemen bekannt gemacht werden. Außer man umgeht das Problem mit vielen Zugriffsfunktionen auf den nach außen hin unsichtbaren Inhalt eines Structs.
Wenn man also sein "Problemlöser" klar geglidert in funktionellen Einheiten hat, ist meine Include Strategie sehr elegent und sehr sauber.
-
NDEBUG schrieb:
Diese Systeme mach ich einfach alle einmal miteinander in defs.h bekannt und dann ist schön.
Wenn eigentlich voneinander entfernte Subsysteme etwas miteinander zu tun haben müssen, wird letztendlich etwas Modularisierung/Abstraktion zugunsten von Performance oder Einfachheit geopfert. Das ist an sich kein Problem, aber ich würde in solchen Fällen trotzdem einzelne Includes in die Quelldateien schreiben. Allein schon um diese Abhängigkeiten zu dokumentieren.
Ansonsten sind all deine Subsysteme in Wirklichkeit nur ein einziges monolithisches Programm, das zwar übersichtlich auf mehrere Quelldateien verteilt ist, aber dessen Bestandteile nur schwierig ausgetauscht oder wiederverwendet werden können.
Nein. Ich include lieber einmal gewisse Standardbibliotheken als in 40-50 unterschiedlichen Source-Dateien.
Mit Standardbibliotheken, die fast überall gebraucht werden, kann man das natürlich so machen.
Structs sind häufig "Datenübermittler" und somit selber Teil eines Interfaces. Dann können sie selbstverständlich nicht mehr in den .c Dateien versteckt werden, sondern müssen den zusammenarbeitenden Systemen bekannt gemacht werden.
Ja, für bestimmte "Wegwerfobjekte" für die Kommunikation zwischen verschiedenen Modulen (zusammengefasste Parameter, Datenpakete...) sind öffentliche Structs nützlich.