Обложка статьи «Умеете ли вы правильно называть функции?»

Умеете ли вы правильно называть функции?

Перевод статьи «Naming Things: Implementer vs. User Names»

Автор перевода — Мария Багулина

Названия функций крайне важны, особенно если вы занимаетесь разработкой пользовательского API. Иногда имя может прекрасно описывать то, что делает функция, но на практике оказывается абсолютно бесполезным. И с этим можно столкнуться даже в стандартных библиотеках. В качестве примеров рассмотрим несколько функций C++20.

Пример 1: std::log2p1()

Начнём с функции std::log2p1().Её код выглядит так:

int log2p1(int i)
{
    if (i == 0)
        return 0;
    else
        return 1 + int(std::log2(x)); 
}

Функция просто возвращает двоичный логарифм числа плюс один, о чём и говорит её название.

Но какая от этого польза?

На самом деле std::log2p1(x) возвращает количество битов, необходимых для хранения x. Это действительно нужная функция, но её название совсем не отражает суть выполняемой операции.

Пример 2: std::bless()

Если вы плохо знакомы с языком C++, то вот быстрое введение в его объектную модель.

В С++ существует такая штука, как указатель — переменная, в которую записывается адрес другого объекта в памяти. Над указателем можно выполнять арифметические действия, но только если он является элементом массива. Почему так? Потому что нельзя вычитать или прибавлять что-то к произвольному указателю — это не имеет смысла, так как расположение объектов в памяти неизвестно.

int obj = 0;
int* ptr = &obj;

++ptr; // Неопределённое поведение

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

Проблема сводится не к глупым программистам, которые зачем-то складывают указатели (ведь это не запрещено), а к самому языку С++. Поэтому Ричард Смит, исследователь из Google, предложил добавить в стандарт функцию std::bless(void* ptr, std::size_t n),чтобы при необходимости автоматически выделять массив памяти и тем самым разрешить вопрос с арифметикой для указателей.

Имя bless, разумеется, никак не сообщает обо всех этих нововведениях, поэтому было решено придумать другое название для функции. За дело взялся комитет по развитию языка C++: в качестве кандидатов они выдвинули верси иimplicitly_create_objects() и implicitly_create_objects_as_needed() («неявно создать объекты» и «неявно создать объекты при необходимости»). Имена вполне логичны, ведь функция делает именно то, что в них сказано. Но, согласитесь, если бы вы не знали предысторию, то совершенно не поняли, зачем нужно создавать какие-то объекты.

Пример 3: std::popcount()

Напоследок ещё одна стандартная функция C++ 20. Просто посмотрите на её имя и попробуйте угадать, что она делает. Кажется, что-то считает? Может быть, это связано со стековыми операциями pop и push?

Скорее всего, вы не догадаетесь, если только уже не знаете о низкоуровневой инструкции с таким же названием. Функция popcount (сокращённо от «population count») подсчитывает количество установленных битов в машинном слове.

С одной стороны, это было бы отличное имя, если бы все разработчики знали названия ассемблерных битовых операций. Но будет ли оно очевидно новичку, который не в курсе таких тонкостей?

Как же следует называть функции?

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

Вряд ли вы подумаете: «Так, мне нужно вычислить двоичный логарифм плюс один, нет ли случайно стандартной функции для этого?». Скорее в вашей голове будет мысль: «Теперь мне нужно знать, сколько битов требуется для хранения этого значения». И ваш запрос в Google будет выглядеть примерно как: «bit width function C++» — почему бы тогда вместоlog2p1()не использовать имяbit_width()?

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

Happy end

Раз уж мы затронули тему C++20, спешим сообщить хорошие новости: кажется, комитет по развитию языка всё-таки переименует std::log2p1()вstd::bit_width(). Но проблема именования затрагивает не только стандартные библиотеки. Поэтому если ваш проект предполагает, что его кодом кто-то будет пользоваться в дальнейшем, выбирайте говорящие имена и смотрите на функции с точки зрения пользователей.