Die STL ist ein kritischer Teil der C ++ - Welt. Die meisten Implementierungen stammen aus den anfänglichen Bemühungen von Stepanov und Musser.
Meine Frage ist angesichts der Kritikalität des Codes gegeben, und es ist eine der Hauptquellen für Menschen, Beispiele für gut geschriebenes C ++ sowohl für Ehrfurcht- als auch für Lernzwecke anzusehen: Warum sind die verschiedenen Implementierungen der STL so ekelhaft anzusehen - verschlungen und Im Allgemeinen gute Beispiele dafür, wie man C ++ - Code aus ästhetischer Sicht nicht schreibt.
Die folgenden Codebeispiele würden die Codeüberprüfung an den Orten, an denen ich gearbeitet habe, aus Gründen nicht bestehen, die von der Benennung der Variablen, dem Layout, den Makros und der Verwendung von Operatoren abweichen, die mehr als einen einfachen Blick erfordern, um herauszufinden, was tatsächlich geschieht.
template<class _BidIt> inline
bool _Next_permutation(_BidIt _First, _BidIt _Last)
{ // permute and test for pure ascending, using operator<
_BidIt _Next = _Last;
if (_First == _Last || _First == --_Next)
return (false);
for (; ; )
{ // find rightmost element smaller than successor
_BidIt _Next1 = _Next;
if (_DEBUG_LT(*--_Next, *_Next1))
{ // swap with rightmost element that's smaller, flip suffix
_BidIt _Mid = _Last;
for (; !_DEBUG_LT(*_Next, *--_Mid); )
;
_STD iter_swap(_Next, _Mid);
_STD reverse(_Next1, _Last);
return (true);
}
if (_Next == _First)
{ // pure descending, flip all
_STD reverse(_First, _Last);
return (false);
}
}
}
_Ty operator()()
{ // return next value
static _Ty _Zero = 0; // to quiet diagnostics
_Ty _Divisor = (_Ty)_Mx;
_Prev = _Mx ? ((_Ity)_Ax * _Prev + (_Ty)_Cx) % _Divisor
: ((_Ity)_Ax * _Prev + (_Ty)_Cx);
if (_Prev < _Zero)
_Prev += (_Ty)_Mx;
return (_Prev);
}
Bitte beachten Sie, dass ich die Benutzeroberfläche nicht kritisiere, da sie sehr gut gestaltet und anwendbar ist. Was mich beunruhigt, ist die Lesbarkeit der Implementierungsdetails.
Ähnliche Fragen wurden bereits gestellt:
Gibt es eine lesbare Implementierung der STL?
Warum ist die STL-Implementierung so unlesbar? Wie hätte C ++ hier verbessert werden können?
Hinweis: Der oben dargestellte Code stammt aus dem MSVC 2010-Algorithmus und den Warteschlangenheadern.
quelle
Antworten:
In Bezug auf die Variablennamen müssen Bibliotheksimplementierer "verrückte" Namenskonventionen verwenden, z. B. Namen, die mit einem Unterstrich gefolgt von einem Großbuchstaben beginnen, da diese Namen für sie reserviert sind. Sie können keine "normalen" Namen verwenden, da diese möglicherweise von einem Benutzermakro neu definiert wurden.
In Abschnitt 17.6.3.3.2 "Globale Namen" §1 heißt es:
(Beachten Sie, dass diese Regeln Header-Wachen verbieten, wie
__MY_FILE_H
ich sie schon oft gesehen habe.)quelle
_Names
sind für die Implementierung reserviert. Wenn Sie also solche Makros haben, ist es auf Ihrem Kopf. Auf der anderen Seite sollten Sie nicht gezwungen sein zu erraten, wie eine STL-Implementierung entscheiden könnte, intern einen Bezeichner zu verwenden, für den Sie kein Makro erstellen sollten, zumal dies von Implementierung zu Implementierung variieren kann.Neil Butterworth, jetzt als "anon" aufgeführt, lieferte einen nützlichen Link in seiner Antwort auf die SO-Frage "Gibt es eine lesbare Implementierung der STL?" . Zitiert dort seine Antwort:
Siehe auch die anderen Antworten in diesem Thread.
Wie auch immer, der größte Teil des STL-Codes (mit STL meine ich hier die STL-ähnliche Teilmenge der C ++ - Standardbibliothek) ist Vorlagencode und muss als solcher nur Header sein, und da er in fast jedem Programm verwendet wird, lohnt es sich, diesen zu haben Code so kurz wie möglich.
Somit ist der natürliche Kompromisspunkt zwischen Prägnanz und Lesbarkeit am Prägnanzende der Skala viel weiter entfernt als bei "normalem" Code.
Darüber hinaus ist in der Standardbibliothek die systemunabhängige Ansicht des Anwendungscodes mit dem zugrunde liegenden System verbunden, wobei alle Arten von compilerspezifischen Dingen verwendet werden, von denen Sie als Anwendungsentwickler am besten Abstand nehmen sollten.
quelle
Variablennamen aus dem Grund, dass dies Standardbibliothekscode ist, und es sollten reservierte Namen für Implementierungsdetails in Headern verwendet werden. Folgendes sollte nicht die Standardbibliotheken brechen:
#define mid #include <algorithm>
Daher können Standardbibliotheksheader daher nicht
mid
als Variablenname verwendet werden_Mid
. Die STL war anders - sie war nicht Teil der Sprachspezifikation, sondern wurde definiert als "Hier sind einige Überschriften, verwenden Sie sie wie Sie wollen".Ihr oder mein Code wäre andererseits ungültig, wenn er
_Mid
als Variablenname verwendet würde, da dies ein reservierter Name ist - die Implementierung darf Folgendes tun:#define _Mid
wenn es sich so anfühlt.
Layout - meh. Sie haben wahrscheinlich einen Styleguide, sie folgen ihm wahrscheinlich mehr oder weniger. Die Tatsache, dass es nicht zu meinem Styleguide passt (und daher meine Codeüberprüfung nicht bestehen würde), ist nichts für sie.
Operatoren, die schwer zu erarbeiten sind - für wen schwierig? Code sollte für die Leute geschrieben werden, die ihn pflegen, und für GNU / Dinkumware / wer wahrscheinlich nicht will, dass Leute in den Standardbibliotheken loslassen, die nicht rätseln können
*--_Next
auf einen Blick . Wenn Sie diese Art von Ausdruck verwenden, gewöhnen Sie sich daran, und wenn Sie dies nicht tun, werden Sie es weiterhin schwer finden.Ich werde Ihnen jedoch sagen, dass
operator()
Überlastung Kauderwelsch ist. [Bearbeiten: Ich verstehe, es ist ein linearer Kongruenzgenerator, der sehr generisch ausgeführt wird. Wenn der Modul "0" ist, bedeutet dies, dass nur der natürliche Umlauf des arithmetischen Typs verwendet wird.]quelle
std::pair
wannfirst
nichts geändert wird?*--
eine vernünftige Operatorkombination in Betracht zieht und Sie dies nicht tun, werden die meisten Leute GNU folgen und sie daher lesen können. Persönlich stört mich das nicht so sehr: Ich denke natürlich nicht, dass Code absichtlich dunkel sein sollte, aber ich denke auch nicht, dass eine so wichtige Komponente wie die Standardbibliotheken in einer so kniffligen Sprache wie C ++ eingesetzt werden sollte Redewendungen zu erfinden und zu verwenden, nur weil Anfänger sie anfangs schwer finden.Implementierungen variieren. libc ++ zum Beispiel ist viel augenschonender. Es gibt immer noch ein bisschen Unterstrich. Wie andere angemerkt haben, sind die führenden Unterstriche leider erforderlich. Hier ist die gleiche Funktion in libc ++:
template <class _Compare, class _BidirectionalIterator> bool __next_permutation(_BidirectionalIterator __first, _BidirectionalIterator __last, _Compare __comp) { _BidirectionalIterator __i = __last; if (__first == __last || __first == --__i) return false; while (true) { _BidirectionalIterator __ip1 = __i; if (__comp(*--__i, *__ip1)) { _BidirectionalIterator __j = __last; while (!__comp(*__i, *--__j)) ; swap(*__i, *__j); _STD::reverse(__ip1, __last); return true; } if (__i == __first) { _STD::reverse(__first, __last); return false; } } }
quelle
while
anstellefor
der Schleifen und schneidet die Debug-Unterstützungsmakros aus. Alle diese Probleme fallen in die Kategorie "Trivial Coding Standard Issues", und keines ist besonders wichtig (mit Ausnahme der Debug-Support-Makros. Wenn Sie jedoch Debugging-Unterstützung wünschen, müssen Sie entweder zwei Implementierungen schreiben oder Makros zum bedingten Kompilieren verwenden es in).Ich vermute, ein Teil des Grundes ist, dass der Code in der STL stark optimiert ist. Bei der Art des implementierten Codes ist die Leistung viel wichtiger als die Lesbarkeit. Weil sie so weit verbreitet sind, ist es sinnvoll, sie so schnell wie möglich zu machen.
quelle
Um das hinzuzufügen, was die Leute bereits gesagt haben, ist der Stil, den Sie sehen, der GNU-Stil. Hässlich? Vielleicht liegt das im Auge des Betrachters. Aber es ist ein streng definierter Stil, der den gesamten Code ähnlich aussehen lässt, anstatt sich daran zu gewöhnen.
quelle