LINUX.ORG.RU

Грёбанная отложенная отладка... Как избегать грубых ошибок?

 , , ,


1

6

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

Например я определил формат для работы с GPIO. Использовать в качестве условия и в качестве команд массивы знаковых целочисленных чисел, содержащих в модуле числа номер порта GPIO, а в знаке состояние порта GPIO. Для хранения в самом объекте текущего состояния использовал бинарный массив который передавал через функцию синхронизации. И обнаружил что ещё давно написал эту функцию принимающую Integer[] (массив целочисленных знаковых), а при синхронизации передавал в неё Boolean[] (бинарный массив). Из за чего тестовый объект Box1 просто не реагировал на GPIO.

Так же я обнаружил ошибку:

      If IntegerArrayGPIO[a] > 0 Then
        ' True
        WriteGPIO(Abs(IntegerArrayGPIO[a]), True) ' Функция Abs использована в данной строе для единообразия
      Endif
      If IntegerArrayGPIO[a] > 0 Then
        ' False
        WriteGPIO(Abs(IntegerArrayGPIO[a]), False)
      Endif
Второе условие было идентично первому.

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

Что вы делаете когда сталкиваетесь с невозможностью или трудностью тестировать большую часть функций сразу после написания?

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

slon
()
Ответ на: комментарий от slon

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

Я уже привык к идиотам которые не имеют своих мозгов и подобно зомби повторяют «бейсик - это плохо, потому что так говорят крутые программисты».

rezedent12 ☆☆☆
() автор топика

Enjoy your dynamic typing.

Нужны тесты. Юнит- и интеграционные.

Zenom ★★★
()

Пиши на C++ или C# и проверяй пвс-студией :)

deadNightTiger ★★★★★
()

Чтобы избежать проблем с

в качестве команд массивы знаковых целочисленных чисел, содержащих в модуле числа номер порта GPIO, а в знаке состояние порта GPIO

достаточно перестать страдать херней и открыть для себя структуры.

Stil ★★★★★
()
Ответ на: комментарий от Stil

в качестве команд массивы знаковых целочисленных чисел, содержащих в модуле числа номер порта GPIO, а в знаке состояние порта GPIO

достаточно перестать страдать херней и открыть для себя структуры.

Они излишни в данном случае.

rezedent12 ☆☆☆
() автор топика

в модуле числа номер порта GPIO, а в знаке состояние порта GPIO

Нулевой GPIO мимо ещё не пробегал?

PolarFox ★★★★★
()
Ответ на: комментарий от PolarFox

Нулевой GPIO мимо ещё не пробегал?

Нуль согласно этому правилу игнорируется.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от PolarFox

Нулевой GPIO мимо ещё не пробегал?

Будет смешно, если окажется, что ТС об этом таки подумал и сдвинул нумерацию GPIO на 1 чтобы избежать этой засады :) А в функциях лезущих в железо у него типа *(GPIO_BASE) = bit << gpio_num - 1. Ну чтобы совсем всё запутать и чтоб жизнь интереснее была.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

Будет смешно, если окажется, что ТС об этом таки подумал и сдвинул нумерацию GPIO на 1 чтобы избежать этой засады :) А в функциях лезущих в железо у него типа *(GPIO_BASE) = bit << gpio_num - 1. Ну чтобы совсем всё запутать и чтоб жизнь интереснее была.

На Raspberry pi нулевого GPIO нет, минимальный номер 2.

rezedent12 ☆☆☆
() автор топика

Что вы делаете когда сталкиваетесь с невозможностью или трудностью тестировать большую часть функций сразу после написания?

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

Тяжесть тестирования функций или модулей - это признак низкого качества кода.

Если без страшных слов - открой для себе автоматические тесты. Начать нужно хотя бы с юнит тестирования.

Shtsh ★★★★
()
Последнее исправление: Shtsh (всего исправлений: 1)
Ответ на: комментарий от rezedent12

И обнаружил что ещё давно написал эту функцию принимающую Integer[] (массив целочисленных знаковых), а при синхронизации передавал в неё Boolean[] (бинарный массив).

Я уже привык к идиотам которые не имеют своих мозгов и подобно зомби повторяют «бейсик - это плохо, потому что так говорят крутые программисты».

Имхо, бейсик плох не тем, что это «не круто», а тем, что это не строгий язык, где легко можно определить функцию с параметром int[] и передать ей bool[], и бейсик не то что не сгенерирует ошибку, но даже не предупредит.

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

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

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

Вопрос эффективности — отдельный. И тут для одного проекта достаточно явы, а для другого необходим си. Но это уже другая история.

aureliano15 ★★
()
Ответ на: комментарий от aureliano15

Имхо, бейсик плох не тем, что это «не круто», а тем, что это не строгий язык, где легко можно определить функцию с параметром int[] и передать ей bool[], и бейсик не то что не сгенерирует ошибку, но даже не предупредит.

Вообще то массивы в gambas являются объектами. И это скорее ошибка реализации, а не стандарта. Я так и не понял почему это происходит.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от Stanson

А ты внезапно не догадываешься, что далеко не все GPIO могут быть выведены на разъём?

Мне нужны выведенные на разъём.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от PolarFox

Типизация статическая, но не строгая? Или все объектные типы не типобезопасны.

Я не проверял. Завтра проверю присваивание объектов. Возможно это ошибка исполнения в IDE.

rezedent12 ☆☆☆
() автор топика

Второе условие было идентично первому.

Это работа для человека-PVS-Studio!

i-rinat ★★★★★
()

функцию принимающую Integer[] [...] передавал в неё Boolean[]

Что вы делаете когда сталкиваетесь с невозможностью или трудностью тестировать большую часть функций сразу после написания?

как все нормальные люди: пользуюсь статической типизацией, а не какой-то там чмошной.

Rastafarra ★★★★
()
Ответ на: комментарий от rezedent12

Они излишни в данном случае.

жесть ))

--- парни, я яйца дверью прищемил!

--- ну так отпусти дверь-то...

--- это излишне в данном случае.

Rastafarra ★★★★
()
Ответ на: комментарий от rezedent12

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

За такую конструкцию нужно бить по морде ссаными тряпками:


if cond then
endif
if not cond then
endif


А это вообще что-то из Нарнии: «Функция Abs использована в данной строе для единообразия».

andreyu ★★★★★
()

Что вы делаете когда сталкиваетесь с невозможностью или трудностью тестировать большую часть функций сразу после написания?

Не делаю так.

Использовать в качестве условия и в качестве команд массивы знаковых целочисленных чисел, содержащих в модуле числа номер порта GPIO, а в знаке состояние порта GPIO.

Нужно больше макарон - откажись от функций, только gosub, только хардкор.

И обнаружил что ещё давно написал эту функцию принимающую Integer[] (массив целочисленных знаковых), а при синхронизации передавал в неё Boolean[] (бинарный массив).

А вот если бы ты использовал «избыточные здесь» структуры, то такого косяка не получилось бы.

no-such-file ★★★★★
()
Ответ на: комментарий от andreyu

За такую конструкцию нужно бить по морде ссаными тряпками:

Я знаю что правильнее в другом случае было бы

if cond then
...
else
...
endif
Но ведь надо игнорировать нуль. Иначе тогда всякий нуль будет восприниматься как отрицательное значение.

А это вообще что-то из Нарнии: «Функция Abs использована в данной строе для единообразия».

Орфографические ошибки в комментарии.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от PolarFox

Типизация статическая, но не строгая? Или все объектные типы не типобезопасны.

Провёл эксперименты. Оказывается все классы реализующие массивы являются потомком одного класса который полностью описывает все их методы. А скрытый метод _get через который можно реализовать обращение к классу как к массиву, возвращает переменную типа Variant. А при операциях с переменными данного типа преобразование происходит автоматически. Таким образом между классами массивов действительно отсутствует типобезопасность. Однако попытка присвоить массиву типа Integer[] экземпляр класса не относящегося к встроенным классам массивов приводит к ошибке несоответствия типов. Таким образом делаю вывод что проверка типов при операциях со встроенными классами массивов отключена в целях ускорения, так как по сути из за одинаковой структуры полей они представляют собой один класс.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от rezedent12

Но ведь надо игнорировать нуль. Иначе тогда всякий нуль будет восприниматься как отрицательное значение.

Тут нужно определить, насколько часто cond может быть равен 0. И какая из веток ( <0 или >0 ) выполняется чаще. Зная эти входные данные можно оптимизировать условие так, что бы сброс конвеера происходил реже.

Но в любом случае if <0 и if >0 нужно заменить на if <0 else if >0. Дабы не проверять условие второго if, если первое условие было истино.

andreyu ★★★★★
()
Ответ на: комментарий от rezedent12

Орфографические ошибки в комментарии.

Я не про ошибки в комментарии, а про использование Abs() ради «красоты».

andreyu ★★★★★
()
Ответ на: комментарий от rezedent12

А тащить малину чтобы разруливать питание на насосах не избыточно? Мне прям не верится, что я дожил до embedded с гнутым бейсиком в мозгах.

kravzo ★★
()
Ответ на: комментарий от andreyu

Тут нужно определить, насколько часто cond может быть равен 0. И какая из веток ( <0 или >0 ) выполняется чаще. Зная эти входные данные можно оптимизировать условие так, что бы сброс конвеера происходил реже.

Я решил жертвовать производительностью ради читаемости кода. Чаще будет выполнятся ветка False. Ибо каждый цикл будет опрашиваться 3 кнопки и реле протока регистрирующее расход.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от kravzo

Мне прям не верится, что я дожил до embedded с гнутым бейсиком в мозгах.

Дожил. Но вообще то она будет не только рулить питанием, но ещё считать кассу и показывать на телевизоре остаток средств.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от rezedent12

Я решил жертвовать производительностью ради читаемости кода.

На мой взгляд, else в данном случае только повышает читабельность. А если будет чаще выполняться ветка «False», то стоит поменять местами условия и использовать else.

andreyu ★★★★★
()
Ответ на: комментарий от andreyu

На мой взгляд, else в данном случае только повышает читабельность. А если будет чаще выполняться ветка «False», то стоит поменять местами условия и использовать else.

Я их поменяю местами. Но если использовать Else, то внутри Else придётся запихать ещё один If проверяющий условие >0.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от rezedent12

Но если использовать Else, то внутри Else придётся запихать ещё один If проверяющий условие >0.

Именно. Но Else позволит не выполнять вторую бесполезную проверку, если условие уже выполнено в If.

andreyu ★★★★★
()
Ответ на: комментарий от Shtsh

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

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 1)
Ответ на: комментарий от rezedent12

считать кассу

Сомнительное решение.

показывать на телевизоре остаток средств

Показывать цифири можно гораздо дешевле и надежнее.

kravzo ★★
()
Ответ на: комментарий от andreyu

На самом деле этот код должен быть всего с одним if, как-то так (хз как это аналогично сделать на gambas, пример плюсовый)

if (auto val = IntegerArrayGPIO[a]) {
  WriteGPIO(std::abs(val), val > 0);
}

mashina ★★★★★
()
Последнее исправление: mashina (всего исправлений: 1)
Ответ на: комментарий от mashina

Да, скорее всего и на бейсике можно точно так же его переписать.

andreyu ★★★★★
()
Ответ на: комментарий от andreyu

Спасибо. Я бы сам не додумался, ибо не мыслю так, что бы использовать результаты сравнения напрямую.

rezedent12 ☆☆☆
() автор топика
Последнее исправление: rezedent12 (всего исправлений: 1)
Ответ на: комментарий от rezedent12

Я в плюсах не разбираюсь, поясни как он выполняется.

Точно так же, как и твой пример, отличия только в структуре кода:

* длинный и трудночитабельный кусок кода IntegerArrayGPIO[a], который в тексте используется несколько раз, вынесен в простую локальную переменную val (можно обозвать как хочется).

* Исключение нуля из работы выполнено одним действием val != 0, а не парой из {val < 0, val > 0}, и вызов WriteGPIO(...) полностью унифицирован.

val as Integer = IntegerArrayGPIO[a]

If val != 0 Then
  WriteGPIO(Abs(val), val > 0)
Endif
Итого, получили простой код в котором значительно труднее ошибиться. В плюсовом варианте декларирование локальной переменной и проверка на неравенство нулю объединены в if (auto val = ...).

Чем меньше делаешь copy+paste, тем меньше вероятность при очередном paste и специализации кода под локальные условия сделать что-то не так. В данном случае наверняка сначала написал первый If..EndIf и потом клонировал его во второй забыв доспециализировать условие.

Чем меньше делаешь ветвлений (т.е. If.. EndIf с похожим кодом внутри), тем, опять таки, труднее ошибиться и легче проверять код перечитыванием. Ещё у тебя размножено IntegerArrayGPIO[a] по коду - тут ошибки не сделал, но из-за этой copy + paste просаживается читабельность.

mashina ★★★★★
()
Ответ на: комментарий от mashina

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

rezedent12 ☆☆☆
() автор топика

Сколько времени прошло со старта проекта твоего? Ну твоя часть по этой мойке.

dk-
()
Ответ на: комментарий от rezedent12

Сколько еще планируешь затратить? Ну без учета факторов типа «он так ничего и не купил и не запускается».

dk-
()
Ответ на: комментарий от dk-

Сколько еще планируешь затратить?

Из деталей надо будет разве что несколько реле ещё докупить для полной развязки схемы с сырым климатом автомойки.

rezedent12 ☆☆☆
() автор топика
Ответ на: комментарий от dk-

А у того как успехи?

Он до весны отложил сборку и открытие.

rezedent12 ☆☆☆
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.