среда, 7 сентября 2011 г.

Если не strcmp...

Опять встретилась проблема в коде. Не то, чтобы очень уж страшная проблема, некоторые даже думают что тем самым они наоборот вносят ясность в код. Но я, как обычно, против.

Давайте посмотрим на следующий код, который я для примера выдрал из ядра linux:
if (!strncmp(name, p, k) && p[k] == '=') {
 	p += k + 1;
	...
Вроде бы ничего особенного... Что же мне может не нравится в таком замечательном коде?

Глядя на этот код, лично я, никак не могу понять каким боком там стоит логическое отрицание? Проблема возникает, когда я пытаюсь этот код прочитать...

Если не strcmp... тогда кто?
Если strcmp ложно... а в чем собственно заключается истина?

Проблема в том, что результат функции не логический. Функция возвращает конкретную разницу, число. В спецификации написано открытым текстом, функция возвращает меньше, больше и равно нулю. Почему бы не написать просто: Если результат вызова strcmp равен нулю то...
if (strcmp(...) == 0) {
	...
Это не проблема работоспособности кода, это проблема понятности кода. Конечно все программисты знают что делает функция strcmp и что она возвращает. Но давайте представим на секунду, что мы видим совершенно незнакомую функцию: if (!foobar(...)) Как вы думаете, что проверяет это условие? Кто нибудь может сходу ответить?

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

Но, и самое главное, если правильно ее использовать в условии, можно сразу, не задумываясь ответить на вопрос - что же она возвращает. foobar(...) == '\0' - Эта функция возвращает char и вероятно наткнулась на символ окончания строки. foobar(...) == nullptr А эта возвращает нулевой указатель (Мы все знаем, что NULL в C++ - это плохо, а 0 очень похож на int :) ), Если же мы видим сравнение с нулем - это значит что нас интересует именно ноль сам по себе. Как число, как константа.

Конечно, никому в здравом уме не придет в голову писать if (foobar(...) == false)... Именно для этого случая существует логическое отрицание. И только в случае логического значения можно не писать явное сравнение.

По моему в давно пора перестать рассматривать ноль, как false, а не ноль, как true... Это было в прошлом веке.

Очень часто встречается, например, со стандартными библиотеками си:
int fd = open(...);
if (fd >= 0)
Ну какой ноль??? В мане серым по черному написано, что в случае ошибки функция возвращает -1, почему бы не написать условие явно?
if (fd == -1) {
	error;
Хотя, может быть это и не самый удачный пример (много этих стандартов в мире юникс, может быть где-то и не -1?).

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

А применяя вместо хитрых трюков типизированные константы, типа '\0', nullptr мы даем читателю подсказки к пониманию кода. С понятными именами у него не должно возникнуть необходимости к тому, чтобы полезть искать прототип, или хуже того - изучать реализацию.