Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Обсуждение: JTN002 - MinUnit -- a minimal unit testing framework for C  (Прочитано 14771 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« : 11-11-2015 04:39 » 

https://forum.shelek.ru/index.php/topic,28557.msg299437.html#msg299437

Dale, так почему он настаивает на использовании

do {} while(0)

вместо обычных

{}

- я так и не понял )  По ссылке тоже не нашёл объяснения, почему именно в виде цикла. Не, я понимаю, что из-за нуля это такие же скобки, но тем не менее - почему настаивается на цикле ?

Разве что защита от "случайного" попадания внутрь макроса continue или break - но это каким надо быть криворуким, чтобы так произошло


А сам макрос я бы спроектировал так, чтобы список тестов выглядел так

Код:
static char* all_tests()
{
     mu_run_test(test_foo,"error, foo != 7", foo == 7);
     mu_run_test(test_bar,"error, bar != 5", bar == 5);
     return 0;
}
« Последнее редактирование: 11-11-2015 07:57 от RXL » Записан

darkelf
Молодой специалист

no
Offline Offline

« Ответ #1 : 11-11-2015 06:30 » 

Dale, так почему он настаивает на использовании

do {} while(0)

вместо обычных

{}

- я так и не понял )  По ссылке тоже не нашёл объяснения, почему именно в виде цикла. Не, я понимаю, что из-за нуля это такие же скобки, но тем не менее - почему настаивается на цикле ?
Я так понял, потому-что после while (0) надо обязательно ставить ';', и если его не будет, то будет ошибка компиляции, а если пропустить ';' после {} - компилятор ничего не скажет.
Записан
zubr
Гость
« Ответ #2 : 11-11-2015 09:41 » 

Использование одноразового цикла, имхо, удобно как раз с использованием continue, break вместо порой громоздких вложенных if
Записан
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 11-11-2015 10:11 » 

Dale, так почему он настаивает на использовании

do {} while(0)

вместо обычных

{}

- я так и не понял )  По ссылке тоже не нашёл объяснения, почему именно в виде цикла. Не, я понимаю, что из-за нуля это такие же скобки, но тем не менее - почему настаивается на цикле ?

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

Пример: условный оператор выглядит так
Код: (C)
if (condition)
  stmt1;
else
  stmt2;

Если в качестве stmt1 использовать макрос, который раскрывается фигурными скобками, получим нечто вроде:

Код: (C)
if (condition)
  {
    ....
  };
else
  stmt2;

, что является синтаксической ошибкой. В случае "одноразового" цикла то же самое выглядит так:

Код: (C)
if (condition)
  do {
    ....
  } while (0);
else
  stmt2;

Это позволяет использовать макрос не только изолированно, но и в контексте условных операторов, циклов etc.

Кстати, как-то я высказал мнение (подкрепленное цитатой из Страуструпа), с которым не все согласились, что макрос - довольно проблемная конструкция, и по возможности следует обходиться другими средствами (благо в C++ без использования макросов можно обойтись почти всегда). Это - одна из многих иллюстраций подобных проблем. Они, конечно, решаемы, но отнюдь не изящно.

А сам макрос я бы спроектировал так, чтобы список тестов выглядел так

Во-первых, в модульном тестировании сложились определенные устои; например, тест должен быть самодостаточным, т.е. должно быть достаточно прочитать исходник test_foo, чтобы понять, что он тестирует, а не искать параметры его вызова. Но это лишь вопрос вкуса и привычек.

Во-вторых, что более существенно, в одном тесте может быть несколько ассертов. Тогда придется формировать в макросе список условий проверки и список сообщений об ошибках, да еще и следить, чтобы списки соответствовали друг другу. IMHO лишний геморрой, не способствующий ни удобству при написании тестов, ни легкости их чтения (не говоря о том, что проблемно написать поясняющие комментарии). Не забываем, что тесты - это еще и документация, причем не в последнюю очередь!
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #4 : 11-11-2015 15:03 » 

Dale, понятно теперь

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

Aether
Специалист

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 11-11-2015 15:34 » 

Кстати, как-то я высказал мнение (подкрепленное цитатой из Страуструпа), с которым не все согласились, что макрос - довольно проблемная конструкция, и по возможности следует обходиться другими средствами (благо в C++ без использования макросов можно обойтись почти всегда). Это - одна из многих иллюстраций подобных проблем. Они, конечно, решаемы, но отнюдь не изящно.
Я макросы тоже не люблю использовать, но иногда бывает, что они нужны. Ооочень редко
Почему в С++ можно обойтись без макросов, а в С нет? По мне, сложные макросы вносят путаницу, чаще проще написать отдельную программу для подсчёта, например, таблицы инволюты, чтобы потом присоединить её к целевому проекту, нежели сочинять весьма непростые для стороннего понимания конструкции генерирования этих таблиц при помощи макросов.
Записан
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 11-11-2015 17:55 » 

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

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

Почему в С++ можно обойтись без макросов, а в С нет?

Как заметил Алексей++, "они нужны. Ооочень редко"(С). Давайте разберем несколько типичных случаев использования макросов и решим, нужны они реально или нет.

1. Защита заголовочных файлов от множественных включений:
Код: (C)
#ifndef MYHEADER_H
#define MYHEADER_H
.
.
.
#endif

В различных реализациях компиляторов имеются прагмы, которые препятствуют повторному включению заголовка при обработке препроцессором (нечто вроде #pragma once), но они не портабельны, как и полагается прагмам. В данном случае использование макроса вполне оправданно, поскольку правильно работает на любой платформе. К тому же идиома давно устоялась и вызовет недоумения разве что у самого зеленого новичка.

2. Определение констант:
Код: (C)
#define PI 3.1415927

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

Иногда "сишники" определяют отдельные константы через enum, но это далеко не всегда дает хороший стиль в итоге.

3. Повторяющиеся области кода:
Код: (C)
#define UGLY_MACRO { \
  a();  \
  b();  \
  c();  \
}

(Хотя мы уже умудрены опытом и знаем, что всю эту лабуду нужно обернуть одноразовым циклом, если не хотим нарваться на невразумительную ошибку при компиляции; впрочем, сейчас речь не об этом).

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

4. Можно приспособить макрос для генерации вариантов кода в зависимости от некоего условия (примеры приводить не буду, ибо громоздко). Гораздо лучше с этим справятся перегруженные функции, шаблоны (template), а также виртуальные методы.

Некоторые из перечисленных языковых средств имеются не только в C++, но и в поздних редакциях C (например, C99). Словом, чем дальше язык от стандарта Кернигана и Ричи, тем меньшую роль в нем играют макросы, хотя совсем на ноль все же не сходят.

По мне, сложные макросы вносят путаницу

Совершенно верно. Кроме того, их бывает трудно раскрывать в уме, чтобы понять, чем именно недоволен компилятор. А уж тестировать макросы и подавно неблагодарное занятие.
« Последнее редактирование: 07-02-2016 23:03 от Dale » Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #7 : 12-11-2015 04:43 » 

Dale, я где-то встречал мнение, что современным компиляторам C++ даже и не обязательно указывать inline, они вроде сами всегда при включенной оптимизации пытаются определить, нужно ли сделать вызов или встроить код
Записан

RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #8 : 12-11-2015 11:41 » 

Добавлю, что макросы еще используются для переопределения функции, например для дебаговой и релизной сборки:

Код: (C++)
int _debug(char* s);

#ifdef XXX
#define debug(s) _debug(s)
#else
#define debug(s)
#endif

debug("zhopa!")
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #9 : 12-11-2015 14:22 » 

Dale, я где-то встречал мнение, что современным компиляторам C++ даже и не обязательно указывать inline, они вроде сами всегда при включенной оптимизации пытаются определить, нужно ли сделать вызов или встроить код

Да, спецификатор inline по мере совершенствования оптимизации становится шумовой лексемой, как, к примеру, и register. Компилятор сам догадается использовать регистр под переменную, если есть возможность; если же возможности нет, то и register в исходном тексте ни на что не повлияет. То же касается inline: как говорится, ученого учить - только портить.

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

Хорошее применение макроса для C. При программировании в объектном стиле я бы предпочел использовать полиморфизм, тем более что в отладочной версии класса можно еще много полезного сделать.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Вад
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 13-11-2015 12:43 » 

Хорошее применение макроса для C. При программировании в объектном стиле я бы предпочел использовать полиморфизм, тем более что в отладочной версии класса можно еще много полезного сделать.
Подразумевается, что в отладочной сборке компилируется один исходник, а в релиз - другой? В противном случае, я не знаю, как в одном и том же файле определить тип сборки, совсем не трогая препроцессора.
Записан
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #11 : 13-11-2015 14:48 » new

Подразумевается, что в отладочной сборке компилируется один исходник, а в релиз - другой?

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

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

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

Код:
mytype foo(...)
{
  ...
  if (DEBUG)
  {
    // отладочный код
  }
  else
  {
    // продукционный код (если нужен)
  }
  ...
}

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

Это навскидку. Может, есть приемы получше. Сам я такими не пользуюсь, поскольку предпочитаю явно выполнять диагностический код в среде модульного/интеграционного тестирования, а не прятать его макросами, благо xUnit сегодня реализован практически для всех языков. IMHO так больше информации получаешь, да и в более удобной форме. После перехода на TDD не могу припомнить случая, когда реально требовалась бы отладочная версия кода, отличная от продукционной.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines