Scope ala (package, internal) in C++ möglich?
-
Wenn du sowas wie package private in java meinst, nein das gibts in c++ nicht.
-
pumuckl schrieb:
Dravere schrieb:
In C++ gibt es höchstens
friend:
http://www.cplusplus.com/doc/tutorial/inheritance/Grüssli
friend ist die stärkste bindung in C++, sogar noch stärker als Vererbung - das sollte man nicht mit einem Zugriffsspezifizierer zwischen public und protected vergleichen

Es geht hier nicht um das normales
publicundprotected. Es geht hier um Dinge wieinternalaus C#. Mitinternalgibst du das Ding dem Ganzen Programm bekannt, das ist deutlich heftiger alsfriend.friendist da geradezu schwach im Vergleich
Edit: Für die, welche kein C# kennen, mag das vielleicht seltsam klingen, deshalb noch eine Ergänzung:
internalist wiepublic, für zum Beispiel die eigene DLL. Für den Anwender der DLL ist es allerdings wie einprivate
@Siassei,
Ich würde, wie Nexus schon sagte, sehr vorsichtig mit sowas umgehen. Normalerweise muss man sowas gar nicht erst machen in C++. Auch in C# und Java sind solche Mittel eigentlich eher selten.Grüssli
-
friend wurde ja schon erwähnt. Davon abgesehen...
In C++ kann man sich auch oft helfen, indem man gewisse Header-Files einfach nicht "public" macht.
Wenn das nicht geht, weil man die "internen" Klassen in "public" Header-Files z.T. vollständig definiert braucht, macht man normalerweise einfach "Detail" Namespaces.Der "User-Programmer" kann die Klassen in solchen "Detail" Namespaces zwar verwenden wenn er unbedingt will. Aber es sollte allgemein bekannt sein, dass man von sowas die Finger lassen sollte. D.h. wer es tut, und sich damit Probleme einhandelt, ist hübsch selbst schuld.
In C++ kann man es so oder so nicht verhindern dass sich jmg. ins Knie schiesst. "Detail" Namespaces sind daher eigentlich recht schön "C++ konform"

-
In C++ kann man es so oder so nicht verhindern dass sich jmg. ins Knie schiesst. "Detail" Namespaces sind daher eigentlich recht schön "C++ konform"
Naja. Imho ist es besser ein Sprachkonstrukt zu haben, um falsches verwenden unmöglich zu machen, als davon auszugehen, dass es richtig benutzt wird. Schliesslich muss die falsche Benutzung ja nicht immer unbedingt Absicht sein. Darum ist ja
constso ein mächtiges Werkzeug. Da gibt es ja viele Situationen, in denen man erst durch den Compiler auf eine falsche Benutzung aufmerksam gemacht würde und dann ist man dem Entwickler der Bibliothek sehr dankbar, dass er mitgedacht hat.
Aber als Abhilfe ist "detail" wahrscheinlich schon das sinnvollste.
-
drakon schrieb:
Naja. Imho ist es besser ein Sprachkonstrukt zu haben, um falsches verwenden unmöglich zu machen, als davon auszugehen, dass es richtig benutzt wird.
Das ist ja das Schwierige.
const? Wird mitconst_castgeknackt.private? Da reicht#define private public.Von
detail-Namensräumen halte ich etwa das Gleiche. Wie gross ist die Wahrscheinlichkeit, dass man unabsichtlich in den Namensraumxyz::detaileindringt? Und wenn man es mit Absicht tut, ist einem sowieso nicht mehr zu helfen.
-
Nexus schrieb:
drakon schrieb:
Naja. Imho ist es besser ein Sprachkonstrukt zu haben, um falsches verwenden unmöglich zu machen, als davon auszugehen, dass es richtig benutzt wird.
Das ist ja das Schwierige.
const? Wird mitconst_castgeknackt.private? Da reicht#define private public.Von
detail-Namensräumen halte ich etwa das Gleiche. Wie gross ist die Wahrscheinlichkeit, dass man unabsichtlich in den Namensraumxyz::detaileindringt? Und wenn man es mit Absicht tut, ist einem sowieso nicht mehr zu helfen.Naja.. Das sind Sachen, mit denen man explizit etwas umgeht.
const_castbesagt das ja alleine mit dem Namen und über defines reden wir hier gar nicht erst.
Aber man könnte ohne es zu wissen auf die Idee kommen detail für sich selbst zu gebrauchen und dann kann es schnell zu Fehler kommen:
//irgend eine lib namespace detail { void foo () { } }; //meine lib namespace detail { void foo (); void b () { foo (); } };Jetzt mag man sagen, dass, wenn man ein foo selber benutzen will, man es ja bestimmt irgendwo definiert haben muss. Aber dann könnte ja das hier passiert sein:
//meine lib namespace detail { //implementier ich später void foo (); void b () { foo (); } };Und schon hat man den graus. Man muss ja nicht bei einfachen Konstrukten vorsichtig sein, sondern alles in Betracht ziehen, was ein übermüdeter und gestresster Programmierer so verbauen kann. Und das Beispiel finde ich gar nicht einmal so weit hergeholt, da, wie schon erwähnt detail sehr oft ja genau für das gebraucht wird und dann kann das schnell passieren. (vor allem in Verbindung mit
using).
-
Naja, möglich ist es schon, aber meiner Ansicht nach kein allzu bedenklicher Fehler. Zumal dort, wo bei dir "irgendeine lib" bzw. "meine lib" steht, normalerweise auch noch Namensräume drum herum stehen. Und über
using namespacebrauchen wir glaube ich auch nicht zu reden.
Abgesehen davon besteht natürlich auch die Möglichkeit, einen komplizierteren, aber einzigarten Namensraum-Bezeichner zu wählen (auch wenn ich das unnötig finde, insbesondere wenn dieser selber in einem Namensraum steht). Aber man darf dann natürlich keinesfalls Boost verwenden.

-
Nexus schrieb:
Zumal dort, wo bei dir "irgendeine lib" bzw. "meine lib" steht, normalerweise auch noch Namensräume drum herum stehen. Und über
using namespacebrauchen wir glaube ich auch nicht zu reden.
Warum nicht? - Die using Direktive ist sehr hilfreich und manchmal unabdingbar. (z.B boost::lambda) und wenn du das in deinem eigenen Namensraum brauchen willst, dann passiert genau das oben.
Ob das ganze ein neues Schlüsselwort rechtfertig sei dahingestellt, aber ich beuge einem möglichem Problem lieber im vornherein vor, als, dass ich mich darauf verlasse, dass etwas richtig benutzt wird.
-
@drakon,
Du machst meiner Meinung nach einen Denkfehler. Es geht hier nicht um globaledetailNamensräume, sondern um solche:namespace libA { // ... namespace detail { // ... } // detail // ... } // ::libAEs geht also um Namensräume wie
::libA::detailoder::libB::detail. Oder zum Beispiel Boost Detailnamensräume:::boost::filesystem::detail::boost::spirit::classic::detail
Und
using namespacesollte man mit äusserster Vorsicht verwenden, das wurde nun schon zig tausend Mal gesagt. Nur weil man zu faul ist drei Buchstaben mehr zu schreiben, sollte man nicht die Namensräume global aushebeln. Wenn du sie aushebelst, dann höchstens lokal in einer Funktion. Oder überusingnur eine einzelne Funktion oder Klasse holen.Wenn es durch ein
using namespacein einem solchen Fall zu einem Fehler kommt, dann ist das für mich gleichbedeutend, als wenn duconst_castoder#define private publicgemacht hättest.Grüssli
-
Und using namespace sollte man mit äusserster Vorsicht verwenden, das wurde nun schon zig tausend Mal gesagt.
Das ist klar, aber wie schon gesagt kann es sein, dass man es braucht (lambda) und ich benutze es auch (vor allem in Verbindung mit boost) sehr gerne, weil ich keine Lust habe jedesmal boost::filesystem::xxx zu schreiben. Das ist auch nicht gerade für die Leserlichkeit förderlich.
-
Trotzdem würde ich versuchen (auch für Lambda-Ausdrücke), möglichst lokale
usings zu verwenden. Dann gerät man auch nicht in Konflikt mit eigenen Namensräumen, da auf globaler Ebene nichts verunreinigt ist.Ausserdem kann man ja auch sowas machen:
namespace fs = boost::filesystem;
-
drakon schrieb:
Das ist klar, aber wie schon gesagt kann es sein, dass man es braucht (lambda) und ich benutze es auch (vor allem in Verbindung mit boost) sehr gerne, weil ich keine Lust habe jedesmal boost::filesystem::xxx zu schreiben. Das ist auch nicht gerade für die Leserlichkeit förderlich.
Dann gäbe es immer noch:
namespace fs = boost::filesystem;Oder eben die lokale Nutzung:
void foo() { using namespace boost::filesystem; // ... }Oder die einzelne
usingAnweisung:void foo() { using boost::lambda::_1; // ... }Alles bereits viel besser, als den globalen Namensraum zuzumüllen.
Grüssli
-
Alles bereits viel besser, als den globalen Namensraum zuzumüllen.
Joa, klar. Es ist auch besser smart Pointer, const, std::string, keine defines usw. zu benutzen, aber gemacht wirds trotzdem. Und wenn man eine falsche Benutzung bereits im vornherein verhindern kann, dann ist das doch eine gute Sache, oder etwa nicht?
-
Dinge zu verhindern, welche falsch genutzt werden, ist ein Ding der Unmöglichkeit. Wenn du anfängst so zu programmieren, dann kann man dein Programm am Ende gar nicht mehr benutzen

Es ist auch nicht die Aufgabe des Autors einer Bibliothek, dafür zu sorgen, dass der Programmierer die Bibliothek richtig verwendet. Es ist sinnvoll, wenn der Autor dafür sorgt, dass die falsche Benutzung erschwert wird, allerdings ist mit einem
detail namespacedafür eigentlich schon zur genüge gesorgt.Wer nicht sinnvoll mit Namensräumen umgehen kann, der ist nunmal einfach selber Schuld oder noch ein Anfänger und muss halt noch lernen.
Grüssli
-
Ich wollte hier nicht "bewerben" es dem Programmierer leicht zu machen, etwas falsch zu verwenden.
Ich meine lediglich dass man es einerseits in C++ nie wirklich wasserdicht hinbekommt, und dass es andrerseits oft viel zu viel Aufwand wäre (bzw. ganz unmöglich) alles mit "friend" zu machen.Daher finde ich "Detail" Namespaces eine gute Möglichkeit. Vor allem weil das eine Konvention ist, die nun wirklich fast jeder kennen sollte. Bzw. ist es auch nicht schwer draufzukommen was gemeint ist, wenn man sowas noch nie gesehen hat.
Schliesslich kommt ja auch wohl keiner auf die Idee _TreeBase (oder wie genau die Klasse heisst) aus der MSVC Standard-Library Implementierung zu verwenden. Und die Klassen leben noch nichtmal in einem Detail Namespace.
----
Die einzige Möglichkeit, die mir jetzt einfällt, wie mit Detail Namespaces unabsichtlich etwas passieren könnte, ist ADL (aka. Koenig Lookup). Und selbst da kann ich im Moment kein gutes Beispiel konstruieren. Und natürlich beschränkt sich das "Problem" ADL auch nicht auf Detail Namespaces. Nicht umsonst wird boost::noncopyable in einem eigenen Namespace definiert, und dann nur per "using" in den "boost" Namespace gezogen.