Hallo Zusammen, kennt Ihr das Problem auch: Ihr habt z.B. einen Treiber, der aus mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten verwenden. Jedes mal gibt es aber ein Problem, weil irgend welche benötigten Dateien fehlen und es dauert relativ lange, bis der Treiber eingepasst ist. Ich überlege mir gerade ein Konzept mit Dummy-Funktionen, so dass man den Treiber erst einmal offline compilieren kann und eventuell Funktionen daraus testweise aufzurufen. Welches Konzept habt Ihr?
Es gibt sehr viele Projekte, in denen Du nur C verwenden darfst. Deshalb ist es von Vorteil einige eigene Libraries in C zu schreiben, damit diese universell verwendet werden können.
Also ich komm bei deinem Problem noch nicht so ganz mit... chris_ schrieb: > Ihr habt z.B. einen Treiber, der aus > mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten > verwenden. Jedes mal gibt es aber ein Problem, weil irgend welche > benötigten Dateien fehlen und es dauert relativ lange, bis der Treiber > eingepasst ist. Auf der einen Seite sagst du das. Das hat erstmal nichts mit deiner Programmiersprache zu tun, wenn du vergisst Dateien einzubinden bzw. an die richtige Stelle zu kopieren. Gehts um das? Also quasi ein Modulmanagment? chris_ schrieb: > Ich überlege mir gerade ein Konzept mit Dummy-Funktionen, so dass man > den Treiber erst einmal offline compilieren kann und eventuell > Funktionen daraus testweise aufzurufen. Das versteh ich aber ganz anders. Hier geht es eher um die Treiberintegration in deiner Programmiersprache bzw. um den eigentlichen Treiber, also wie du den testest? Was genau ist denn jetzt dein Problem?
chris_ schrieb: > kennt Ihr das Problem auch: Ihr habt z.B. einen Treiber, der aus > mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten > verwenden. Jedes mal gibt es aber ein Problem, weil irgend welche > benötigten Dateien fehlen Dann ist der Code nicht vernünftig modular geschrieben. Das Strukturieren von Code-Dateien nennt sich Physical Design bzw. Physical Code Organization. Im Prinzip geht es darum erst einmal zu verstehen was echte Module sind, statt die immer wieder anzutreffende Behauptung man würde schon modular entwickeln. Dann geht es darum die Module und eventuell sogar ihre internen Komponenten so zu strukturieren, dass es keine zyklischen Abhängigkeiten gibt (auch nicht direkt zwischen zwei Modulen). Weiter geht es um den Schnittstellen-Entwurf (API Design) und zum Schluss packt man das Ergebnis in eine Library und eine Header-Datei. Wichtige Code-Techniken sind alle, mit denen man feste Kopplungen vermeidet. In C gehen zum Beispiel Callbacks, Inversion of Control (Dependency Injection), Plugins. > Welches Konzept habt Ihr? Sauberen Code schreiben, statt nachträglich zu flicken.
Zwei Ansätze: Du kannst C++ in C "nachbilden" indem du struct verwendest. Es ist ein Kompromiss, der Overhead ist nicht so groß wie in C++, aber da alles auf Zeiger basiert ist es natürlich etwas langsamer und größer. Ansatz zwei sind Callback Routinen, also Zeiger auf Funktionen. Du initialisierst das richtige Modul und rufst immer dieselben Funktionen. Zb. LCD Display. Entweder mit 4 IO oder 8, mit I2C und dawomöglich verschiedene I2C IC's. Die Applikation sieht immer nur die standard C Funktionen und je nach verwendeter Hardware Konfiguration werden unterschiedliche Module aufgerufen.
Das ist so eine seltsame Forderung. Ala man soll schnell fahren können darf aber auf keinem fall einen Motor verwenden. C++ bringt halt für das angesprochene Problem die Lösungen mit sich
NurC schrieb: > Das ist so eine seltsame Forderung. Ala man soll schnell fahren können > darf aber auf keinem fall einen Motor verwenden. C++ bringt halt für das > angesprochene Problem die Lösungen mit sich Nein bringt es nicht. In C++ kann man genauso schlonzigen "modularen" Code schreiben wie in C. Das ist ein guter alter Trugschluss, dass OO bzw. C++ immer alles per Sprachdesign löst. Kann es ja gar nicht, hat ja alle Unzulänglichkeiten von C einfach übernommen. Sauberen, modularen Code kann man in C genauso programmieren wie in C++, damit beschäftigen muss man sich in beiden Fällen. Ein gutes Beispiel wie man modularen Code aufbauen kann sieht man bei Autosar. Da haben sich Leute hingesetzt, die von C ne Ahnung haben und haben aufgezeigt, wie man richtig modular in C programmiert.
>kennt Ihr das Problem auch: Ihr habt z.B. einen Treiber, der aus >mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten >verwenden. Jedes mal gibt es aber ein Problem, weil irgend welche >benötigten Dateien fehlen und es dauert relativ lange, bis der Treiber >eingepasst ist. nein das Problem kenne ich nicht, du kannst ja eine Lib machen die du dann statisch mitlinkst oder was meinst du genau, ist der Treiber jedesmal anzupassen an die HW? Dann solltest du den Treiber aufteilen, in den HW unabhängigen Teil und in einen HW abhängigen Teil.
Hier mal ein kleines Beispiel für das Plug- and Play-Konzept. Die Namensgebung ist an die AUTOSAR-Interface Namen angelehnt. Es gibt - required - provided Client-Server Interfaces. Das System muss quasi die Required-Interfaces zur Verfügung stellen.
Das würde aber mal ganz grass das DRY Prinzip verletzen. Respekt. und die unübliche Namesgebung der Header File. Überleg mal jeder der einen bestimmten Aspekt seiner Datei in den Dateinamen hinterlegt würd das so machen. Schau dir lieber mal Clean Code Regeln an. Die kann man auch für C einhalten.
>Das würde aber mal ganz grass das DRY Prinzip verletzen.
Was genau meinst Du damit?
>Respekt. >und die unübliche Namesgebung der Header File. Da liegt vermutlich ein Missverständnis vor. Die Paketbezeichung "Hallo" habe ich nur als Beispiel verwendet. Du kannst auch "EDF" daraus machen. Vielleicht wäre die Abstraktion einfacher für Dich gewesen, ich hätte das Paket "BEISPIEL" genannt.
chris_ schrieb: > z.B. einen Treiber, ... Gerade für Treiber gibt's ein uraltes Konzept, das (mindestens) seit den ersten Unix-Systemen erfolgreich funktioniert. Alt muß nicht unbedingt schlecht sein. ein Low-Level-Treiber besteht aus nur ganz wenigen Funktionen: device_init() device_open() device_read() device_write() device_close() device_ioctl() Mehr braucht's nicht und wer so ein Treibermodul zum ersten Mal sieht, kann es sofort verwenden. Spezialfälle werden als Einstellungen oder Abfragen in ioctl() abgefackelt und das ist praktisch auch die einzige Funktion, die dokumentiert werden muß.
chris_ schrieb: >>Das würde aber mal ganz grass das DRY Prinzip verletzen. > > Was genau meinst Du damit? Erweitere dein Beispiel um einen fast gleichen 2. Treiber, dann wirst du sehen dass gerade in deiner *_required.h immer wieder dasselbe darin steht. Je nach Sicht gibt es *A*_required.h und *B*_provided.h mit identischen Inhalt. Zusammengefasst meine ich das das gleiche Interface einmal Quelle und einmal senke ist. Aber in unterschiedlichen Dateien völlig identisch geschrieben werden muss. Als Beispiel: Wenn du die Aufrufparameter einer Funktion abänderst musst du 3 Dateien ändern. Prinzipiell ist bei 2 Dateien das DRY Prinzip schon verletzt. Das ist mit ein Grund weshalb modernere Sprachen wie C# zwischen Interface und Implementierung nicht mehr unterscheiden.
Hier noch mal eine neue Version. Ich habe das Paket mal in "BEISPIELPAKET" umbenannt, damit es hier keine Missverständnisse mehr gibt. Es gibt noch ein passendes Test-Paket dazu, dass sozusagen den Unit-Test gleich mitbringt. Außerdem gibt es jetzt ein "PLATFORM"-Paket, dass auf die Low-Level Funktionen des Systems zugreift. Das BEISPIELPAKET selber ist völlig unabhängig. Das Ganze wird über die RTE verbunden. Der Nachteil an der Verbindung über "Defines" ist aber, dass man z.B. mit Eclipse nicht mehr automatisch zu den anderen Funktionen springen kann. ( Der Nachteil haben die echten Implementierungen von AUTOSAR aber leider meistens auch ). Autor: D. F. (Firma: EDF) (derdan) >Wenn du die Aufrufparameter einer Funktion abänderst musst >du 3 Dateien ändern. Meiner Erfahrung nach sind die Änderungen an den Interfaces die größten Probleme in Softwareprojekten. Deshalb müssen die von Anfang an sehr gut überlegt sein und sollten so wenig wie möglich geändert werden.
Ich habe jenachdem unterschiedliche vorgehen. Wenn ich ein Projekt habe, und ich will beliebig viele Module für eine Funktionalität haben, und brauche alles nur bei diesem Projekt, definiere ich ein Interface, und implementiere dieses mehrfach. Dann packe ich die object files (.o files) des Moduls in eine statische library (.a files), sofern es mehr als ein object file ist. Beispiel Interface: https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/headers/DPA/UCS/driver/eth/driver.h Beispiel Implementationen: https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/eth/dummy.c https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/eth/linux.c https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/eth/enc28j60.c Wenn ich mehrere Module mit Abhängigkeiten habe, und ich nur eins brauch, mache ich sowas: Interface: https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/headers/DPA/UCS/adelay.h Implementation: https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/adelay/clock.c https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/adelay/timer.c Verwendung woanders: https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/protocol/tcp/tcp.c Ich habe also das Interface beim Programm definiert, welches die Module verwendet. Wenn ich aber plane, etwas in mehreren Projekten zu nutzen, dann baue ich eine statische Library, die keine Abhängigkeiten zum Projekt hat, und nutze das als Modul. Das Interface ist dann in einer Headerdatei beim Modul definiert. In jeden fall sind bei mir .h und .c files in unterschiedlichen unterverzeichnissen.
:
Bearbeitet durch User
Autor: Markus F. (mfro) >Gerade für Treiber gibt's ein uraltes Konzept, das (mindestens) seit den >ersten Unix-Systemen erfolgreich funktioniert. Alt muß nicht unbedingt >schlecht sein. >ein Low-Level-Treiber besteht aus nur ganz wenigen Funktionen: >device_init() >device_open() >device_read() >device_write() >device_close() >device_ioctl() Das sind aber alles Funktionen, die der Treiber zur Verfügung stellt. In AUTOSAR würde man diese provided client/server interfaces nennen. Einen Treiber in der Art zu schreiben ist kein Problem, weil sich die Interfaces des Treibers zum System nicht ändern ( bzw. die sind hier gar nicht sichtbar ). Ein Problem tritt dann auf, wenn man ein Paket schreiben will was sich auf anderen Pakte bezieht bzw. Funktionen aus anderen Pakten braucht ( sogenannte provided interfaces ).
chris_ schrieb: > kennt Ihr das Problem auch: Ihr habt z.B. einen Treiber, der aus > mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten > verwenden. So herum wird das nie und nimmer etwas. Ich mache das genau umgekehrt: Der Low-Level-Treiber kapselt die darunter liegende HW und stellt "nach oben" eine hardwareunabhängige Schnittstelle zur Verfügung. Damit kann ich alle höheren Programmschichten zumeist ohne Änderungen auf verschiedensten Systemen verwenden. Lediglich der unterste Treiber muß eben an die jeweilige HW angepaßt sein. Manchmal kann man den sogar ganz woanders wiederverwenden, siehe z.B. der SDIO-Treiber für die älteren LPC (Arm7Tdmi) und der SDIO-Treiber für die STM32F1xx. Aber wozu man für einen einzigen Treiber mehrere C-Dateien benötigt, erschließt sich mir nicht. OK, es gibt scharenweise Programmierer, die aus jedem winzigen Furz eine separate Datei machen - und damit die nicht so winzig aussieht, wird noch die GPL in voller Länge eingefügt. Sehr leserlich, sowas. W.S.
ich glaube auch das der Weg den der _chris da gehen sehr aus Holz ist...
Autor: HolzMin (Gast) >ich glaube auch das der Weg den der _chris da gehen sehr aus Holz ist... Dann wäre AUTOSAR auf dem Holzweg. Aber da gibt es ja tatsächlich viele, die das glauben, nicht war? ( chris_ ) Autor: Thomas Winkler (Firma: privat) (diddl) >Ansatz zwei sind Callback Routinen, also Zeiger auf Funktionen. Du >initialisierst das richtige Modul und rufst immer dieselben Funktionen. Callback-Routinen sind tatsächlich ein Ansatz, damit man keine Funktionen "nach unten" ( required interfaces braucht ). Da die Zeiger im RAM gehalten werden müssen, bedeutet das aber einen höheren Speicherverbrauch. Außerdem würde ich noch NULL-Pointer Checks einbauen, damit bei nicht gesetztem Call-Back kein Crash auftritt. Man könnte also so was machen:
1 | void setStdOut(const void* stdOut); |
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.