Wie wird in C gekapselt?
-
Tim schrieb:
~john schrieb:
P.S. Sowohl K&R wie auch die ISO Norm sprechen nie von Modulen.
Die ISO-Norm erwähnt das in C-Kreisen überaus exotische Wort "Compiler" auch nur ein einziges Mal und das in einer Fußnote.
Der Vergleich hinkt, "translation unit" wird in der ISO Norm exzessiv genutzt.
-
~john schrieb:
Interessehalber schrieb:
Wenn ihr alle immer die Weisheit mit Löffeln gefressen habt verstehe ich nicht warum die Artikel bei Wikipedia nicht von euch geändert werden? Wenn nicht von den Profis, von wem dann?
Das Problem an Wikipedia ist, daß sich dort eine Masse Personen tummtelt, die sich mit Kritik an ihrer Haltung schwer tun. Die Folge wäre ein Editwar mit dem Ergebnis, daß das Falsche immer noch dort stünde. Dafür ist mir meine Lebenszeit zu kostbar. Ich hatte das mal mit einer anderen Programmiersprache versucht den Autoren auf Fehler im Artikel hinzuweisen - keine Chance.
Wikipedia gilt in der Wissenschaftsgemeinde grundsätzlich als nicht zitierfähig, weil es sich ständig ändern kann und man zum Teil krude Verdrehungen dort findet. Manchmal taugt es nur als Linksammlung zu einem bestimmten Thema. Bücher und Zeitschriftenartikel sind da eindeutig besser, und in denen findet sich auf viel Müll.
Na das nenne ich mal eine Erklärung. Danke für den Einblick, dass ist mal ein super konstruktiver Beitrag. Er ist informativ, nicht beleidigend oder diskreditierend
-
-
~john schrieb:
Das Problem an Wikipedia ist, daß sich dort eine Masse Personen tummtelt, die sich mit Kritik an ihrer Haltung schwer tun. Die Folge wäre ein Editwar mit dem Ergebnis, daß das Falsche immer noch dort stünde...
Korrekt. Sogar Trivias wie Grundbegriffe der Bearbeitungstechnik (in dem Fall Gleichlauf/Gegenlauf) bleiben ewig falsch stehen.
Nach dem mehrmonatigen Edit- Hickhack ist der Artikel gelöscht worden, obwohl er ansonsten nicht schlecht war.
Das nennt man Mitarbeitermotivation.
-
@richtigso
Ich höre mir lieber SeppJ an als so nen Pseudo-Klugen wie dich, der nicht mal nen Namen hat.
"gefährlichen Halbwahrheiten" <-- ja, sehr gefährlich... Die gefahr solltest du nicht ignorieren. Eine Gefahr zu ignorieren macht sie nur noch gefährlicher.
richtigso schrieb:
Aber ich werde versuche die gefährlichen Halbwahrheiten so genannten "Experten" einfach mal zu ignorieren.
earli schrieb:
Sqwan schrieb:
Und nie mals vergessen auch vor der eigenen Tür zu kehren!
Man muss kein Sternekoch sein, um sagen zu können, wenn's wie Scheiße schmeckt.
Wie Recht du doch hattest!
Und noch mal neben bei: Wikipedia hilft schon sehr oft bei der Arbeit, auch inhaltlich. In der Uni darf ich dennoch nicht drauf verweisen!
Häufig finde ich bei Wiki behauptungen die sehr gut sind, die ich dann nur noch durch Fachliteratur beweisen muss!
-
Daß ich das mal sage
: Kann mal jemand *bitte* den Thread schließen?
Danke!
-
pointercrash() schrieb:
Daß ich das mal sage
: Kann mal jemand *bitte* den Thread schließen?
Danke!das geht mit dem kleinen X in der Fensterecke.
-
-
Eine Möglichkeit, Typen (und insbesondere ihre 'interne' Implementierung) in C zu 'kapseln', ist die Verwendung von Handles.
Jemand, der dann mit so einem C-API arbeiten soll, bekäme dann z.B. derartige 'public' Header-Files:/* --- PUBLIC_API_HEADER.h --- */ typedef struct _tImage* hImage; typedef struct _tMesh* hMesh; typedef struct _tFont* hFont; typedef struct _tDataBase* hDataBase; /* usw. usf.: Wie das API diese Datentypen 'intern' implementiert, ist für den Verwender nicht sichtbar. Alles, was dem API-Verwender zur Verfügung gestellt wird, sind dann nur noch die Schnittstellen, über die er Instanzen dieser Datentypen erzeugen / zerstören kann, und was er sonst noch mit diesen Objekten anstellen kann, wenn sie denn mal existieren. Beispielsweise so: */ hImage CreateImage(char* FileName); void RotateImage(hImage ToRotate, float Angle); void BlendImage(hImage Dest, hImage Src1, hImage Src2); /* ... usw. usf. Das soll nur andeuten, wie so eine 'OOP' - C - API im Idealfall aussieht. */
Der Implementierer der Funktionalität dieser 'Handle-Datentypen' verwendet ebenfalls diesen 'öffentlich sichtbaren' Header, und kann sich dann eine geeignete 'interne' Implementierung eines struct _tImage einfallen lassen, mit der er dann die 'geforderten' Schnittstellen (die aus dem 'öffentlichen' Header ersichtlich sind) implementieren kann.
Also z.B. so: (Sichtweise des Implementierers)/* --- TIMAGE_IMPLEMENTIERUNG.c --- */ #include <PUBLIC_API_HEADER.h> /* Hier nun die Konkretisierung eine _tImage Datentyps: typedef struct _tImage{ void* KnownType; /* Was auch immer sonst noch an Private Members (die ebenfalls Opaque Handles sein koennen!) benoetigt wird, um die geforderten Schnittstellen zu implementieren */ }tImage, *hImage;
Der Verwender des API bekommt den öffentlichen Header + die (kompilierte) Implementierung des API, und braucht sich dann garantiert keine Gedanken mehr darüber zu machen, wie die Implementierung aussieht (da zu 100% 'unsichtbar').
Also in etwa so gelingt die 'Kapselung' in C. (Bei C++ - APIs sehe ich ja als Anwender immer den kompletten internen Aufbau der zur Verfügung gestellten Klassen (und wenn auch private: dabei steht), und die Versuchung ist dann größer, sich die bekannte (oder zumindest vermut-bare) (interne) Funktionsweise einer Klasse zu Nutze zu machen. Einem C-Handle hingegen sehe ich nicht im geringsten an, wie es funktioniert. Der Hersteller der API kann anstelle von Pointer-Handles natürlich auch unsigned-Handles anbieten, die Handles 'Intern' auf ihre Gültigkeit überprüfen usw.
Als Anwender finde ich C-Handles jedenfalls auch 'bequemer' zu handhaben, als derartig gespenstische Donaudampfschiff-Fahrts-Ausdrücke wie:
device->getSceneManager()->getActiveCamera()->drop();
... mit denen man sich häufig bei der Verwendung von C++ - APIs herumschlagen muss und wo bei jeder Codezeile ein halbes Dutzend Null-Pointer Exceptions nicht ausgeschlossen werden kann.
Auch als Herangehens-Weise an größere Projekte (Top-Down-Methode) erweist es sich als sehr fruchtbar, erstmal mit 'Opaquen' Datentypen der höchsten Abstraktions-Ebene die 'Wunsch-' Funktionalität der nächst-niedrigeren Abstraktions-Ebene festzulegen.
Wenn dann z.B. der Implementierer vom obigen Beispiel {tImage} feststellt, dass er zur Erfüllung seines Jobs noch weitere 'Opaque' Datentypen einer noch niedrigeren Abstraktions-Ebene benoetigt, so schreibt er einen entsprechenden Header mit 'seinen' Wuensch-Funktionen & dazugehörigen Datentypen usw.
Schlussendlich gelingt es so allmählich, auch größere Projekte in ihre trivialen Teilaufgaben zu zerlegen. Erstens lässt sich dann der Umfang der Aufgabe besser abschätzen. Zweitens bereitet dann die konkrete Umsetzung keine gröberen Schwierigkeiten mehr (außer recht viel Tipp-Arbeit). Und drittens verliert man dabei das konkrete End-Ziel nie aus den Augen, da man ja von der Zielsetzung ausgegangen ist, um eine geeignete Modularisierung zu realisieren. (Das Gegenteil geschieht bei einigen C++ - APIs, wo 'auf Verdacht' dutzende abstrakte Basisklassen gebastelt werden, die man vielleicht einmal brauchen könnte, und die zwar rein 'imaginär' alles könnten, aber eben 'konkret' für gar nichts optimal zu gebrauchen sind ...mfg
-
Also ist C doch für große Projekte teilweise besser geeignet als C++?
-
Prototyp3 schrieb:
...
Daten hinter Zeigern verstecken ein Geheimnis von C? Nicht wirklich. Siehe pimpl-Idiom.
Prototyp3 schrieb:
Als Anwender finde ich C-Handles jedenfalls auch 'bequemer' zu handhaben, als derartig gespenstische Donaudampfschiff-Fahrts-Ausdrücke wie:
device->getSceneManager()->getActiveCamera()->drop();
In C schreibt halt Ausdrücke(Fahrt(Donaudampfschiff))
drop(getActiveCamera(getSceneManager(device)));
, aber ist das nun besser?
Prototyp3 schrieb:
Auch als Herangehens-Weise an größere Projekte (Top-Down-Methode) erweist es sich als sehr fruchtbar, erstmal mit 'Opaquen' Datentypen der höchsten Abstraktions-Ebene die 'Wunsch-' Funktionalität der nächst-niedrigeren Abstraktions-Ebene festzulegen.
"Für große Projekte muß man dies und jenes (völlig austauschbar) machen."
Da kann man nicht widersprechen, denn wer anders denkt, macht nur zu kleine Projekte.Prototyp3 schrieb:
Das Gegenteil geschieht bei einigen C++ - APIs, wo
Ja, viele Nubes programmieren viel Zeug.
-
Hallo,
ich würde in C kapseln, indem ich einfach die funktionen nach sinn in einer "dem sinn" entsprechenden .c-datei einordne. das ergibt eine wunderschöne baumstruktur.
also im prinzip stattclass irgendeine_klasse
{
};mit
#include irgendeinheader.h
und in irgendeinheader.h bzw. .c dann globale variablen, funktionen, strukturen. muß man halt ordentlich dokumentieren, wo man welche variable und welche funktion untergebracht hat.
ich hab übrigens mal von einer firma gehört, die auf die frage, wie sie das programm so schnell und schlank hinbekommen haben, angab, komplett auf objektorientierte programmierung verzichtet zu haben.
-
Prototyp3 schrieb:
Also in etwa so gelingt die 'Kapselung' in C. (Bei C++ - APIs sehe ich ja als Anwender immer den kompletten internen Aufbau der zur Verfügung gestellten Klassen (und wenn auch private: dabei steht), und die Versuchung ist dann größer, sich die bekannte (oder zumindest vermut-bare) (interne) Funktionsweise einer Klasse zu Nutze zu machen. Einem C-Handle hingegen sehe ich nicht im geringsten an, wie es funktioniert. Der Hersteller der API kann anstelle von Pointer-Handles natürlich auch unsigned-Handles anbieten, die Handles 'Intern' auf ihre Gültigkeit überprüfen usw.
Im Grunde ist das kein schlechter Ansatz. Nur ist man so gezwungen, die Handle-internen Daten dynamisch anzufordern oder zumindest eine Art von Indirektion aufzubauen. So viel zu "OOP in C++ ist langsam".
Deshalb könnte ich mir auch vorstellen, dass Jürgen453213s Aussage, der Verzicht auf OOP hätte zu schnellem Programmcode geführt, in C tatsächlich einen wahren Kern hat. Auch wenn es im Allgemeinen nicht sinnvoll ist, diesen Schluss zu ziehen. Denn nicht zuletzt kann man ja auch in C die privaten Daten direkt in Strukturen schreiben, und dem Benutzer durch ein Member-Präfix oder irgendeine Konvention klar machen, dass er auf diese Daten nicht zugreifen soll.
Aber abgesehen davon: Kapselung dient primär dazu, den normalen Programmierer von Fehlern zu bewahren ("Murphy"), nicht den Bösewicht aufzuhalten ("Machiavelli"). Insofern zähle ich die von dir erwähnte Versuchung nicht wirklich als Argument.
-
volkard schrieb:
Prototyp3 schrieb:
...
Daten hinter Zeigern verstecken ein Geheimnis von C? Nicht wirklich. Siehe pimpl-Idiom.
Unerfahrenen Programmierern mag das Konzept der Verwendung opaquer Datentypen bzw. Handles zur Kapselung der Implementierung eines API durchaus wie ein Geheimnis erscheinen - schon möglich, dass Du davon noch nie etwas gehört hast.
Im übrigen werden dadurch nicht nur vereinzelte Daten vor dem Anwender 'versteckt' bzw. 'geheim gehalten', sondern die gesamte Implementierung des API, also insbesondere die verwendeten Datenstrukturen, Algorithmen usw. - alles Dinge, von deren Existenz der Anwender des API aber auch schon gar nichts zu wissen braucht.
Opaque C-Handles sind quasi eine 'perfekte' Blackbox, vergleichbar einem Radio, bei dem der Anwender zwar an den Knöpfen zur Bedienung des Gerätes interessiert ist, nicht aber an den Drähten und anderen Interna des Gerätes - und mit der Besonderheit, dass man diese Blackbox auch niemals 'aufschrauben' kann, um an den Drähten herumzufummeln - um z.B. aus Teilen des gut funktionierenden Radios eine schlecht funktionierende Waschmaschine zu basteln (das sollte jetzt ein bildhafter Vergleich mit C++ Klassen sein, die 'im Prinzip' 'alles' können, wenn man sie nur oft genug ableitet und erweitert, um einen Datentyp zu erhalten, der 'halbwegs' das tut, was man möchte.Der OOP-Ansatz mit Handles ist ein völlig anderer (und zugleich fehler-vorbeugender als das immer dicker werdende C++-Superobjekt, das zugleich ein Radio als auch eine Waschmaschine sein möchte.)
-
Prototyp3 schrieb:
Also in etwa so gelingt die 'Kapselung' in C. (Bei C++ - APIs sehe ich ja als Anwender immer den kompletten internen Aufbau der zur Verfügung gestellten Klassen
Das sieht man nur dann, wenn es einen Designfehler gibt. Das von Dir für C beschriebene Design würde man in C++ anderweitig umsetzen.
Variante eins:
Abstract Factory mit Rückgabe eines Smart Pointers auf eine Abstract Class. Die konkreten Klassen implementiert man dann unsichtbar von Nutzer. Nur deren Konstruktionsname, -nummer o.ä. muß man bekannt machen.
Variante zwei:
Bridge Pattern (auch Handle/Body oder Pimpl idiom genannt)
Hier wird eine Interface Klasse definiert und die Implementierung in einer separaten Klasse vorgenommen, die der Nutzer nicht sieht.