Подтвердить что ты не робот

Почему код в большинстве реализаций STL настолько запутан?

STL - критическая часть мира С++, большинство реализаций проистекают из первоначальных усилий Степанова и Муссера.

Мой вопрос задан критичностью кода, и он является одним из основных источников для людей, чтобы просмотреть примеры хорошо написанного С++ для благоговения и обучения: почему различные реализации STL настолько отвратительны, чтобы смотреть на - свернутые и в целом хорошие примеры того, как не писать код С++ с эстетической точки зрения.

Приведенные ниже примеры кода не будут проходить проверку кода в местах, где я работал, по причинам, отличающимся от имен переменных, макетов, макросов и использования операторов, которые требуют более простого взгляда, чтобы выяснить, что на самом деле происходит.

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 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

Почему реализация STL настолько нечитаема? Как С++ можно было улучшить здесь?

Примечание. Приведенный выше код берется из алгоритма MSVC 2010 и заголовков очередей.

4b9b3361

Ответ 1

Нейл Баттерворт, теперь представленный как "анон", предоставил полезную ссылку в своем ответе на вопрос SO "Есть ли читаемая реализация STL?" . Процитировать его ответ:

Существует книга The С++ Standard Библиотека шаблонов, в соавторстве с оригинальные дизайнеры STL Степанов и Ли (вместе с П. Дж. Плаугером и Дэвидом Musser), в котором описывается возможное реализация, в комплекте с кодом - видеть http://www.amazon.co.uk/C-Standard-Template-Library/dp/0134376331.

См. также другие ответы в этой теме.

В любом случае, большая часть кода STL (по STL я здесь означает STL-подобный поднабор стандартной библиотеки С++) является кодом шаблона и, как таковой, должна быть только для заголовка, и поскольку она используется почти в каждой программе, она платит чтобы этот код был как можно короче.

Таким образом, естественная точка компромисса между лаконичностью и удобочитаемостью намного дальше на краю конца шкалы, чем с "нормальным" кодом.

Кроме того, стандартная библиотека - это то, где системно-независимое представление кода приложения связано с базовой системой, используя все виды специфических для компилятора вещей, которые вам как разработчику приложений лучше всего избегать.

Приветствия и hth.,

Ответ 2

О именах переменных, разработчики библиотек должны использовать "сумасшедшие" соглашения об именах, такие как имена, начинающиеся с символа подчеркивания, за которым следует буква в верхнем регистре, поскольку эти имена зарезервированы для них. Они не могут использовать "нормальные" имена, поскольку они могут быть переопределены пользовательским макросом.

Раздел 17.6.3.3.2 "Глобальные имена" §1 гласит:

Определенные наборы имен и сигнатур функций всегда зарезервированы для реализации:

  • Каждое имя, содержащее двойное подчеркивание или начинающееся с подчеркивания, за которым следует буква верхнего регистра, зарезервировано для реализации для любого использования.

  • Каждое имя, начинающееся с символа подчеркивания, зарезервировано для реализации для использования в качестве имени в глобальном пространстве имен.

(Обратите внимание, что эти правила запрещают защиту заголовков, например, __MY_FILE_H, которые я видел довольно часто.)

Ответ 3

Переменные имена по причине, что это стандартный код библиотеки, и он должен использовать зарезервированные имена для деталей реализации в заголовках. Не следует нарушать стандартные библиотеки:

#define mid
#include <algorithm>

Таким образом, стандартные заголовки библиотек не могут использовать mid как имя переменной, следовательно _Mid. STL был другим - он не был частью спецификации языка, он был определен как "вот некоторые заголовки, используйте их так, как вы"

С другой стороны, ваш код или мой, был бы недействителен, если бы он использовал _Mid как имя переменной, так как это зарезервированное имя - реализация разрешена:

#define _Mid

если это так.

Макет - meh. У них, вероятно, есть руководство по стилю, они, вероятно, следуют за ним, более или менее. Тот факт, что он не соответствует моему руководству по стилю (и, следовательно, не прошел бы мой обзор кода), ничто для них.

Операторы, которые трудно выработать - трудно кому? Код должен быть написан для людей, которые его поддерживают, и GNU/Dinkumware/, которые, возможно, не хотят, чтобы люди теряли доступ к стандартным библиотекам, которые не могут решить проблему *--_Next с первого взгляда. Если вы используете такое выражение, вы привыкните к нему, и если вы этого не сделаете, вы продолжите его усердно.

Я дам вам, однако, что перегрузка operator() - тарабарщина. [Edit: Я получаю это, это линейный конгруэнтный генератор, выполненный очень общим образом, и если модуль равен "0", что означает просто использовать естественное обертывание арифметического типа.]

Ответ 4

Реализации различаются. libС++, например, намного проще на глазах. Тем не менее, есть еще немного подчёркивающего шума. Как отмечают другие, к сожалению, требуются ведущие подчеркивания. Здесь же функция в libС++:

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;
        }
    }
}

Ответ 5

Я подозреваю, что часть причины заключается в том, что код в STL сильно оптимизирован. Тип выполняемого кода имеет производительность, которая гораздо важнее, чем читаемость. Поскольку они настолько широко используются, имеет смысл сделать их как можно быстрее.

Ответ 6

Чтобы добавить то, что уже говорили люди, стиль, который вы видите, это стиль GNU. Гадкий? Возможно, это в глазах смотрящего. Но это строго определенный стиль, и он делает весь код похожим, а не устойчивым к привыканию.