대부분의 STL 구현의 코드가 왜 그렇게 복잡합니까?
STL은 C ++ 세계의 중요한 부분이며 대부분의 구현은 Stepanov와 Musser의 초기 노력에서 비롯됩니다.
내 질문은 코드의 중요성에 대한 것이며, 사람들이 경외심과 학습 목적 모두를 위해 잘 작성된 C ++의 예제를 볼 수있는 주요 소스 중 하나입니다. STL의 다양한 구현이 왜 그렇게 혐오스러운가요? 일반적으로 미학적 인 관점에서 C ++ 코드를 작성하지 않는 방법에 대한 좋은 예입니다.
아래의 코드 예제는 변수 이름 지정, 레이아웃, 매크로 및 실제로 발생하는 일을 파악하기 위해 단순한 눈 이상이 필요한 연산자의 사용 등 다양한 이유로 제가 작업 한 곳에서 코드 검토를 통과하지 못합니다.
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);
}
인터페이스가 매우 잘 설계되고 적용 가능하기 때문에 나는 인터페이스를 비판하는 것이 아닙니다. 내가 염려하는 것은 구현 세부 사항의 가독성입니다.
유사한 질문이 이전에 제기되었습니다.
STL 구현이 그렇게 읽을 수없는 이유는 무엇입니까? 여기서 C ++를 어떻게 개선 할 수 있었습니까?
참고 : 위에 제시된 코드는 MSVC 2010 알고리즘 및 큐 헤더에서 가져온 것입니다.
이제 "anon"으로 나열된 Neil Butterworth는 SO 질문 "STL의 읽기 가능한 구현이 있습니까?"에 대한 유용한 링크를 제공했습니다. . 그의 대답을 인용하면 :
원본 STL 디자이너 Stepanov & Lee (PJ Plauger 및 David Musser와 함께)가 공동 저술 한 The C ++ Standard Template Library 책이 있습니다.이 책은 코드로 완성 된 가능한 구현을 설명합니다 . http://www.amazon을 참조 하십시오. co.uk/C-Standard-Template-Library/dp/0134376331 .
해당 스레드의 다른 답변도 참조하십시오.
어쨌든 대부분의 STL 코드 (여기서는 STL에 의해 C ++ 표준 라이브러리의 STL과 유사한 하위 집합을 의미 함)는 템플릿 코드이므로 헤더 전용이어야하며 거의 모든 프로그램에서 사용되므로 비용을 지불해야합니다. 가능한 한 짧은 코드입니다.
따라서 간결함과 가독성 사이의 자연스러운 절충점은 "일반"코드보다 규모의 간결함 끝에서 훨씬 더 멀어집니다.
In addition, the standard library is where the system-independent view of application code is connected to the underlying system, utilizing all kinds of compiler-specific things that you as an application developer should best stay away from.
About the variables names, library implementors must use "crazy" naming conventions, such as names starting with an underscore followed by an uppercase letter, because such names are reserved for them. They cannot use "normal" names, because those may have been redefined by a user macro.
Section 17.6.3.3.2 "Global names" §1 states:
Certain sets of names and function signatures are always reserved to the implementation:
Each name that contains a double underscore or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.
Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
(Note that these rules forbid header guards like __MY_FILE_H
which I have seen quite often.)
Variable names for the reason that this is standard library code, and it should use reserved names for implementation details in headers. The following should not break the standard libraries:
#define mid
#include <algorithm>
So standard library headers can't use mid
as a variable name, hence _Mid
. The STL was different - it wasn't part of the language specification, it was defined as "here are some headers, use them as you will"
Your code or mine, on the other hand, would be invalid if it used _Mid
as a variable name since that's a reserved name - the implementation is allowed to do:
#define _Mid
if it feels like it.
Layout - meh. They probably have a style guide, they probably follow it, more or less. The fact that it doesn't match my style guide (and hence would fail my code review) is nothing to them.
Operators that are difficult to work out - difficult to whom? Code should be written for the people who maintain it, and GNU/Dinkumware/whoever probably don't want to let people loose on the standard libraries who can't puzzle out *--_Next
at a glance. If you use that sort of expression, you get used to it, and if you don't you'll continue finding it hard.
I will give, you, though, that operator()
overload is gibberish. [Edit: I get it, it's a linear congruential generator, done very generically, and if the modulus is "0" that means just use the natural wraparound of the arithmetic type.]
Implementations vary. libc++ for example, is much easier on the eyes. There's still a bit of underscore noise though. As others have noted, the leading underscores are unfortunately required. Here's the same function 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;
}
}
}
I suspect part of the reason is that the code in the STL is highly optimized. The sort of code being implemented has performance being much more important then readability. Because they are so widely used it makes sense to make them as fast as possible.
To add on what people have said already, the style you see is the GNU style. Ugly? Perhaps, that's in the eye of the beholder. But it's a strictly-defined style, and it does make all code look similar, as opposed to resistant to getting used to.
'programing tip' 카테고리의 다른 글
문 복사 값을 반환합니까? (0) | 2020.11.22 |
---|---|
Angular 2-하위 모듈 라우팅 및 중첩 (0) | 2020.11.22 |
ASP .NET MVC의 web.config에서 TargetFramework 설정은 무엇을 의미합니까? (0) | 2020.11.22 |
C # 값 / 객체는 언제 복사되고 언제 참조가 복사됩니까? (0) | 2020.11.22 |
자바 스크립트 문자열 유형과 문자열 객체의 차이점은 무엇입니까? (0) | 2020.11.22 |