К вопросу о титановых велосипедах и программировании вообще

GarryC
Offline
Зарегистрирован: 08.08.2016

Я так понял, что это ответ на вопрос о загадочном смысле пересылки значения параметра (типа boolean) в промежуточную переменную (того же типа) с целью последующего сравнения с единицей? Если это так, то ответ явно неудачный.

Клапауций 12345
Offline
Зарегистрирован: 17.05.2020

GarryC пишет:

Я так понял, что это ответ на вопрос о загадочном смысле пересылки значения параметра (типа boolean) в промежуточную переменную (того же типа) с целью последующего сравнения с единицей? Если это так, то ответ явно неудачный.

я тебе здесь #43 ответил, где нужно продолжать задавать вопросы по секундомеру.

Клапауций 1488 пишет:

все свои вопросы по коду секундомера - в тему кода и там вежливо спрашивать

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

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

GarryC пишет:

Конечно же, жду с нетерпением.

Пожалуйста.

Пример получился несколько объёмным, но он показывает самые разные варианты. Структуры SDimensions и SPosition используются как сами по себе, так в качестве полей структуры SDimPos. Та, в свою очередь, используется как сама по себе, так и в качестве поля структуры TheObject. Т.е. мы имеем здесь просто структуру, один и два уровня вложенности. Всем полям присваиваются значения, а затем всё печатается для проверки. И нигде никаких дополнительных точек, везде только одна! Наслаждайся.

Всё это (кроме Serial, разумеется) работает как в С, так и в С++ безо всяких изменений - проверяй.

// Пример использования безымянных структур в Си и С++

//	Размер объекта
#define SDimensions	struct { int width; int height; }

//	Положение объекта
#define SPosition	struct { int x; int y; int z; }

// Используем объявленные структуры сами по себе
SDimensions dim;
SPosition pos1, pos2;

// Объявляем структуру, содержащую и размер, и положение
// т.е. имеющую вложенные структуры
#define SDimPos	struct { SDimensions; SPosition; }

// Используем структуру, содержащую и размер, и положение
SDimPos dimpos;

// Объявляем структуру, для объекта, имеющего и размер, и положение
// т.е. имеющую два уровня вложенности структур
struct TheObject {
	char name[10];
	int weigth;
	SDimPos;
};

// Объявляем объект
TheObject obj;

//
// Теперь проверим, как всё это работает
//
void setup() {
	// заполнение структуры dim;
		dim.width = 123;
		dim.height = 321;
	// заполнение структуры pos1;
		pos1.x = 21;
		pos1.y = 13;
		pos1.z = 2;
	// заполнение структуры pos2;
		pos2.x = 12;
		pos2.y = 31;
		pos2.z = 345;
	// заполнение структуры dimpos;
		dimpos.width = 1;
		dimpos.height = 2;
		dimpos.x = 3;
		dimpos.y = 4;
		dimpos.z = 5;
	// заполнение структуры obj;
		strcpy(obj.name, "Fuck off!");
		obj.width = 32;
		obj.height = 45;
		obj.x = 12;
		obj.y = 1;
		obj.z = 10;
		obj.weigth = 666;

	Serial.begin(9600);

	// печать структуры dim;
		Serial.println("Structure dim");
		Serial.print("dim.width="); Serial.println(dim.width);
		Serial.print("dim.height="); Serial.println(dim.height);
	// печать структуры pos1;
		Serial.println("Structure pos1");
		Serial.print("pos1.x="); Serial.println(pos1.x);
		Serial.print("pos1.y="); Serial.println(pos1.y);
		Serial.print("pos1.z="); Serial.println(pos1.z);
	// печать структуры pos2;
		Serial.println("Structure pos2");
		Serial.print("pos2.x="); Serial.println(pos2.x);
		Serial.print("pos2.y="); Serial.println(pos2.y);
		Serial.print("pos2.z="); Serial.println(pos2.z);
	// печать структуры dimpos;
		Serial.println("Structure dimpos");
		Serial.print("dimpos.x="); Serial.println(dimpos.x);
		Serial.print("dimpos.y="); Serial.println(dimpos.y);
		Serial.print("dimpos.z="); Serial.println(dimpos.z);
		Serial.print("dimpos.width="); Serial.println(dimpos.width);
		Serial.print("dimpos.height="); Serial.println(dimpos.height);
	// печать структуры obj;
		Serial.println("Structure obj");
		Serial.print("obj.name="); Serial.println(obj.name);
		Serial.print("obj.weigth="); Serial.println(obj.weigth);
		Serial.print("obj.x="); Serial.println(obj.x);
		Serial.print("obj.y="); Serial.println(obj.y);
		Serial.print("obj.z="); Serial.println(obj.z);
		Serial.print("obj.width="); Serial.println(obj.width);
		Serial.print("obj.height="); Serial.println(obj.height);
}

void loop() {  
}

Маленькое замечание: если бы речь шла только о С++ сам бы я никогда не стал так писать печать. Портянку в строках №№63-92 пишут только те, кто не знает, что структура в С++ - это нормальный, полноценный класс (т.е. спецы вроде тебя). У меня бы в С++ печать выглядела бы вот так:

Serial.println("Structure dim");
Serial.println(dim);
Serial.println("Structure pos1");
Serial.println(pos1);
Serial.println("Structure pos2");
Serial.println(pos2);
Serial.println("Structure dimpos");
Serial.println(dimpos);
Serial.println("Structure obj");
Serial.println(obj);

Разумеется, для этого нужно кое-что изменить в структурах. Что и как - это тебе домашнее задание. Заодно и материал маленько изучишь.

GarryC
Offline
Зарегистрирован: 08.08.2016

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

GarryC,

Вы опять бредите. Володя сейчас вернётся и совершенно справедливо натычет Вас носом в Ваши слова

GarryC пишет:

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

которые, на самом деле верны, но только если заменить слово "невозможно" на выражение "я не умею" :-)

Я Вам уже советовал:

ЕвгенийП пишет:
Если уж Вы взялись писать учебный материал по какому-то вопросу, так изучите его

Вы же, вместо этого, упорно продолжаете спорить с людьми, уровень знаний которых не сопоставим с Вашим. 

И ещё, прекратите постить дешёвые отмазки, типа "я там просто с прямым углом Си перепутал" - некрасиво. Нет ничего зазорного в том, что Вы не знали о том, что struct и class - это практически одно и тоже. Многие этого не знают - узнал, поблагодарил и поехали дальше. А публикуя отмазки, Вы теряете уважение читателей.

GarryC
Offline
Зарегистрирован: 08.08.2016

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

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

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

class DelayType2{
public:
  void DelayStart(unsigned long time) { start_=millis(); time_=time; };
  int DelayIsOver(void) { return ((millis() - start_) > time_);};
private:
  unsigned long start_,time_;
};

DelayType2 delay1;
delay1.DelayStart(time1);
	...
if (delay1.DelayIsOver() {DoSomething();};

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

Нам удалось удовлетворить требования второго пользователя, но что делать с первым. Конечно, мы можем написать 2 класса и пусть каждый берет тот, который ему удобнее, но есть возможность ответить ожиданиям сразу всех. Первый способ - создать 2 набора методов, но это усложняет (неоправданно) интерфейс класса, уж лучше тогда два класса. А вот второй способ намного удобнее - это перегрузка методов, вот он, класс, объединяющий оба подхода:

class DelayType {
public:
  void DelayStart(unsigned long time) { start_=millis(); time_=time; };
  int DelayIsOver(void) { return ((millis() - start_) > time_);};

  void DelayStart(void) { start_=millis(); time_=0; };
  int DelayIsOver(unsigned long time) { return ((millis() - start_) > time);};
private:
  unsigned long start_,time_;
};

DelayType delay1,delay2;
delay1.DelayStart(time1);
delay2.DelayStart();
	...
if (delay2.DelayIsOver(time1) {DoSomething();};
if (delay1.DelayIsOver() {DoSomething();};

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

У этого подхода есть недостатки:
1) имеется переменная time_, которая не всегда используется (по мнению автора исходного кода, используется, ведь ей присваивается значение 0, но мы то с Вами понимаем ...).
2) возможно смешанное использование методов, проверять на этапе компиляции невозможно, но к фатальным последствиям это не приведет.
3) код не чистый, имеется нарушение принципа DRY (вся работа должна проводится в одном месте), хотя это относится только к реализации, пользователь не будет учиться плохому.

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

class DelayType {
public:
  void DelayStart(unsigned long time) {start_=millis(); time_=time; };
  int DelayIsOver(void) { return DelayIsOver(time_);};

  void DelayStart(void) { DelayStart(0); };
  int DelayIsOver(unsigned long time) { return ((millis() - start_) > time);};
private:
  unsigned long start_,time_;
};

Примечание:10 лет назад я в таком случае написал бы макросы-обертки (в Линуксе так и делают до сих пор), но сейчас оптимизация в компиляторе настолько хороша, что оберточные функции не стоят вообще ничего.

 

GarryC
Offline
Зарегистрирован: 08.08.2016

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

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

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

ЕвгенийП пишет:
Володя сейчас вернётся и совершенно справедливо натычет Вас носом
А вот хренвам! Я потратил полчаса на написание примера вложенных структур, а в благодарность получил "мог бы и не писать"!

Тратить ещё полчаса на пример передачи в функцию, чтобы получить тоже самое? Оно мне сдалось? Если бы он спросил как их передать - я бы ответил, но он же не спросил, безапелляционно заявил, что "невозможно". Ну,  "невозможно" - значит "невозможно", мне-то совершенно пофиг что там для ТС возможно, а что - нет. В общем, с меня, на сегодня, вполне достаточно благодарности читателей за потраченные время и усилия, потому новых примеров не будет.

Кстати, я ещё сдуру, хотел дать комментарий, что техника использования безымянных классов наиболее практически полезна с юнионами, но слава Богу, поленился писать. А то бы мы ещё долго выслушивали, что ТС их (юнионы) с Си перепутал, а про то, что они тоже классы - он, разумеется, знал :-)

Клапауций 12345
Offline
Зарегистрирован: 17.05.2020

Ворота пишет:

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

очень это всё странное.

преподы хотят благодарности при отсутствии результатов вместо результатов.

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

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Клапауций 1488 пишет:

очень это всё странное.

У каждого из нас есть вещи, которые нам понять не дано - рылом не вышли. Так что даже не пытайся - не поймёшь.

Клапауций 12345
Offline
Зарегистрирован: 17.05.2020

Ворота пишет:

У каждого из нас есть вещи, которые нам понять не дано - рылом не вышли. Так что даже не пытайся - не поймёшь.

ясен пень - у меня же нет базовых [подставить нужное].

сходить почитать ТС в тему титанового велосипеда прежде, чем продолжать ему отвечать я запретил.

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

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

точно. у меня же нет этих самых... базовых. :D

Pyotr
Offline
Зарегистрирован: 12.03.2014

Ворота, спасибо за пример. Канешн спасибо в карман не положишь и в стакан не нальешь)), но всеж... Думаю что многие присоединятся к словам благодарности.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Понимаешь, Клап, нам с тобой сраться абсолютно не о чем. Ты не программист и чего-то не знаешь из того, что я знаю, я не радиомонтажник и не умею так как ты паять и так компоновать схемы как ты - но это ведь не проблема совсем! За тобой я ни разу не замечал претензий на "эксперт во всём" в отличие от Великого или от нынешнего ТС, потому к тебе я отношусь вполне с уважением. Не вижу причин и условий для срача. Всё путём - мелкая пикировка, "задирки", "подколки" и наезды, чтобы мозги не тухли - это всегда пожалуйста, всегда полезно, а срач ... о чём нам сраться-то? Вроде, не о чем. Как знаешь, в общем.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Спасибо, Pyotr!

Можете решить задачу, которую я оставил ТС как домашнее задание? А ещё лучше показать ТС как безымянные поля параметрами  в функции передавать (то, что по его мнению "невозможно")? Попробуйте - это прикольная задача.

Клапауций 12345
Offline
Зарегистрирован: 17.05.2020

Ворота пишет:

Понимаешь, Клап


ок.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Ворота пишет:

Можете решить задачу, которую я оставил ТС как домашнее задание? А ещё лучше показать ТС как безымянные поля параметрами  в функции передавать (то, что по его мнению "невозможно")? Попробуйте - это прикольная задача.

Неее, сам не смогу. С плюсами у меня нет цельного представления. Да и не с плюсами тоже. Но могу у Петровича подсмотреть))

Green
Offline
Зарегистрирован: 01.10.2015

Ой, сколько много возвышенного!). По крестьянски делаю так. Минимум-минимор, безо всяких ++:
#define timer_init(x)     static uint32_t x = millis()
#define timer_above(x, y) (millis() - x >= y && (x = millis(), true))
ну и в тесте:
timer_init(t);
if (timer_above(t, 1000)) {
  //every 1000 ms
}

 

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Green пишет:

По крестьянски делаю

А я по-пролетарски - просто пишу в лоб безо всяких макросов :-)

Green
Offline
Зарегистрирован: 01.10.2015

Макросы ведь для наглядности. Написал и забыл. И для платформонезависимости. К примеру, нет у меня Ардуино фреймворка, нет миллиса, есть системный тик. Тогда меняем только макросы и всё. Исходный текст остаётся без изменений.

#define timer_init(x)     static uint16_t x = 0
#define timer_above(x, y) (++x >= ms2sys(y) && !(x = 0))

 

Green
Offline
Зарегистрирован: 01.10.2015

Ну, если о пролетариях, нужно учитывать что и плюсы то не везде есть.)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Green пишет:

нужно учитывать что и плюсы то не везде есть.)

Что да, то да. Это минусов везде - как дерьма за баней.

GarryC
Offline
Зарегистрирован: 08.08.2016

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

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

// некоторая внешняя единица компиляции
typedef struct {
    unsigned int x,y;
} Coord;
extern void CoordSet(Coord *coord, int x,int y);
extern float CoordLength(Coord coord);
typedef struct {
    unsigned char r,g,b;
} Color;
// началась наша программа
typedef struct {
    Coord coord;
    Color color;
} Point;
... 
    Point point;
    point.coord.x=3; point.coord.y=4; // необходимы две точки
    CoordSet(&point.coord,3,4);        // хотя правильнее это делать через функцию
    CoordLength(point.coord);           
    return 0;
}

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

2. Разработчики С11 пошли навстречу нашим желаниям и разрешили нам использовать анонимные структуры в виде вставки (именно этот вариант рассматривал Ворота в своем примере), опять таки смотрим код:

// некоторая внешняя единица компиляции осталась прежней
// началась наша программа
typedef struct {
    struct { unsigned int x,y;};
    struct { unsigned char r,g,b; };
} Point;
... 
    Point point;
    point.x=3; point.y=4;        // ура, лишних точек нет
    CoordSet(&point.??,3,4);     // а что делать тут - проблема
    CoordLength(point.??);    // а особенно тут       
    return 0;
}

Нам удалось убрать лишние точки, но очень дорогой ценой:
первое - нам пришлось копировать описания внешних структур внутри нашей (строки 4 и 5) и теперь любое изменение реализации во внешнем файле требует изменения и нашего файла, причем следить за этим должен программист, а не компилятор.
второе и третье -непонятно, что делать с вызовом функций в строке 10 и 11 - у вложенной структуры нет имени, поэтому указать ее нет возможности.

3. Разработчики С11 (тут я не вполне уверен, идет ли речь о С вообще, либо о GCC компиляторе в частности, а это две большие разницы) опять идут нам навстречу и вводят флаг компиляции -fms-extensions, который позволяет нам использовать уже готовые описание без необходимости их повторения:

typedef struct {
    Coord;
    Color;
} Point;

что исключило первую проблему, но вторая (с указателем на вложенную структуру) осталась. Конечно, мы можем поэкспериментировать, написав код вроде:

   CoordSet(&point.x,3,4); // ура, работает !
   CoordSet(&point.y,3,4); // хм, тоже работает, странно как то ...

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

    CoordSet((Coord*)&point.x,3,4);

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

Со строкой 11 дело обстоит еще хуже, поскольку конструкцию

   CoordLength(*(Coord*)&point.x);

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

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

4. Продолжая двигаться навстречу пользователям, разработчики GCC (ну или С11, я уже и не знаю) вводят еще один флаг компиляции -fplan9-extensions, который позволяет нам правильно решить задачу в следующем виде:

   CoordSet(&point.coord,3,4);  // никаких предупреждений     
   CoordLength(point.coord);    // и тут тоже 

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

Но в этой большой бочек меда есть маленькая ложка дегтя - данные флаги компиляции доступны далеко не в каждом компиляторе и даже не в каждой версии gcc. Я провожу свои исследования на сайте godbolt.org и там ни один clang не взял такой синтаксис, ни один AVR GCC не взял, хотя ARM GCC почти все, кроме самого старого, отработали.

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

А, вообще то, спасибо за задание, оказалось интересным.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

GarryC пишет:

Мне тут дали домашнее задание

Домашнее задание было - сделать нормальную печать таких структур, ты чем-то не тем занимался :-)

GarryC пишет:

относится к языку С

Обращаю Ваше внимание, что здесь форум, посвящённый Arduino , а не чему-нибудь другому, поэтому разумнее было бы исследовать С++. Тем более, что в нём гораздо больше интересного на эту тему.

GarryC пишет:

далеко не в каждом компиляторе 

Опять же, здесь форум по Arduino и нас не особо интересуют "ВСЕ" компиляторы. Хотя, должен заметить, что в С++ все поставленные задачи решаются средствами языка как такового без хитрозадых опций.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

GarryC пишет:

вдруг мы хотим странного ...

Ничего странного.

Представьте структуру для конфига. В ней есть поля разных типов, в то числе и битовые поля. Разве странно сделать, чтобы битовые поля были доступны наравне с другими конфигурационными параметрами, а не через дополнительное поле? По-моему - это естественно, а дополнительное поле - костыль.

GarryC
Offline
Зарегистрирован: 08.08.2016

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

Если вернуться к исходному коду, то мы придумали, как использовать вторую внутреннюю переменную, третья просто не нужна и вроде как все понятно. Но ... в исходном коде используются два метода (один с дополнительным параметром) и еще прямое обращение к внутренней переменной, которое у нас заменено вторым методом. Почему в исходном коде три сущности торчат наружу из класса, а у нас только два, может мы что то делаем не так, надо бы понять. Дело в том, что автором рассматривался несколько иной пользователь, нежели наш - он засекает момент времени, а затем ожидает окончание различных интервалов, от него отсчитываемых. В чем же дело, спросите Вы, ведь наш первый вариант полностью покрывает требования такого пользователя, смотрим код:

DelayType delay;
delay.DelayStart();
	...
if (delay.DelayIsOver(time1) {DoSomething1();};
if (delay.DelayIsOver(time2) {DoSomething2();}; // и таких строк можно написать много ...

Тут на первый план выходит такая вещь, как эффективность кода. В нашем методе сравнения (внутри его, пользователь об этом может не знать, но мы то знаем) в строке 07 вызывается функция millis(), которая может быть длительной и вызывать ее каждый раз при активации метода сравнения расточительно. Поэтому автор и принял такое (неоднозначное) решение, применив метод, известный, как предварительное вычисление. Минус данной конкретной реализации в том, что мы возложили на хрупкие плечи пользователя обязанность самостоятельно вызывать отдельный метод для подсчета интервала перед собственно сравнением, хотя он нас об этом не просил, это ему не нужно. Что мы можем сделать:
1) вообще не обращать на проблему внимания, ведь для пользователя с одним интервалом это точно не нужно, а остальные потерпят - в общем приемлемо, но как то не аккуратненько.
2) взять заботу о подобной ситуации на себя и попытаться разрешить ее, что мы и попробуем сделать далее.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Вот я тебя, канеш, уважаю безмерно, но никада больше не кури эту гадость. Смени дилера. Ну пожалуйста....

Клапауций 12345
Offline
Зарегистрирован: 17.05.2020

GarryC пишет:

Что мы можем сделать:

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Клапауций 1488 пишет:
не понятно, что ты подразумеваешь под хрупкими плечами ползателя
Это?

Клапауций 12345
Offline
Зарегистрирован: 17.05.2020

ЕвгенийП пишет:

Клапауций 1488 пишет:
не понятно, что ты подразумеваешь под хрупкими плечами ползателя
Это?

это

Клапауций 12345
Offline
Зарегистрирован: 17.05.2020

мне, вообще, не ясно в чём суть проблемы:

есть некий код в открытом доступе. 
содержимое кода настолько банально, что автор, при желании, не сможет предъявить кому либо претензий нарушений авторства.
практически невозможно запретить вносить в этот код исправления, улучшения/ухудшения и публиковать их на форуме.
вместо этого я продолжаю читать страницы полубреда, перемежающегося с претензиями к выдуманным недостаткам.
на предложения общения по существу - полный игнор.
 
!поэтому, админам - потребуйте у ТС справку от психиатра.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Кстати, GarryC

я вот всё стесняюсь спросить ... мы тут с Вашей подачи изо всех силов изучаем камасутру ... тьфу ... MISRу, стремимся так сказать соответствовать и писать кошерные коды, а Вы - наш, можно сказать, учитель и, не побоюсь сказать, Апостол MISRы! Вы что себе позволяете? В нескольких жалких строчках поста #72 десятки (!!!) нарушений священного писания ... да что это меня сегодня заносит ... MISRы!

Расскажите сообществу! В чём дело? MISRA устарела? Вы в ней разочаровались? Или это просто религия для лохов, а нормальные пацаны её только проповедуют, а соблюдать им необязательно?

Будьте так любезны, объяснитесь! :-)

GarryC
Offline
Зарегистрирован: 08.08.2016

Объясняюсь (я вообще очень любезен, как Вы могли заметить): вообще то моей целью никак не было пропаганда данного (весьма и весьма неплохого стандарта), Вы могли бы заметить, что я обсуждаю только подход к программированию, общие принципы проектирования, но никак не конкретную реализацию. Хотя в своей повседневной практике я стараюсь (без необходимости) не нарушать правила, изложенные в упомянутом Вами MISRA, по крайней мере те, которые считаю существенными (да, я такой и сам решаю, что существенно, а что нет). В разрезе поднятого вопроса - я (и не я один) считаю допустимым применение комментариев в стиле // (конечно, при одновременном запрещении триграфов), считаю допустимым писать более одной лексемы на строке.
Возможно, это неправильно, я не претендую здесь на абсолютную истину, но нарушение именно данных правил приводит к написанию более компактного (в смысле представления на экране, в разумных пределах) и более понятного кода, что и является моей основной целью.
Если Вы заметили именно подобные отклонения от стандарта, то существуют утилиты, которые позволяют перед помещение кода в репо его отформатировать в соответствии с принятым стандартом. Если же есть у Вас замечания относительно нарушения сути MISRA в моих опусах, укажите и я, конечно же, внесу соответствующие исправления.
Ну и, поскольку это не промышленный код, я далеко не всегда (вообще никогда) в своем учебном коде использую (совершенно необходимые для финальной версии) квалификаторы типа const.

PS. Когда я говорю, что я очень любезен, я не имею в виду, что буду поддерживать диалог с людьми, которые склонны на замечания отвечать в стиле "А че такого" (к Евгению это ни в коей мере не относится).

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Не, ну, так низзя!

GarryC пишет:
существуют утилиты, которые позволяют перед помещение кода в репо его отформатировать в соответствии с принятым стандартом ... это не промышленный код, я далеко не всегда (вообще никогда) в своем учебном коде использую (совершенно необходимые для финальной версии) квалификаторы типа const

Даже странно слышать такое от человека, который когда-то писал совершенно правильные слова:

GarryC пишет:
хороший программист даже маленькую программу напишет правильно, просто потому, ЧТО ПО ДРУГОМУ НЕ УМЕЕТ.

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

GarryC пишет:
В разрезе поднятого вопроса - я считаю допустимым применение комментариев в стиле // ), считаю допустимым писать более одной лексемы на строке.

Если бы только это! Там до фига ещё чего. Например, вот только то, что сразу резануло по глазам:

  1. строки №№ 3 и 5 в самом первом скетче (и далее повсеместно!!!) - нарушено правило №6.3 («typedefs that indicate size and signedness should be used in place of the basic numerical types»);
  2. во втором сверху скетче наблюдается нарушение правила №9.1 («All automatic variables shall have been assigned a value before»);
  3. в том же втором сверху скетче, в строке №9 производится присваивание знаковых целых беззнаковым переменным, что есть нарушение целого ряда правил (в простейшем случае - правила №10.6 («A “U” suffix shall be applied to all constants of unsigned type»));
  4. в самом первом скетче поля x, y структуры - беззнаковые, а параметры функции их назначения - знаковые, что есть нарушение целого ряда правил (например №№ 10.1 и 10.2);
  5. в строках №№2,7,11 первого скетча  (и далее много где) определены структуры без тэгов, что есть нарушение правила №5.4. Конечно, можно оправдаться тем, что  изначальная задача была исследовать именно безымянные структуры, но в этих строках (и во многих других) использование безтэговой структуры для решения задачи не требуется.

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

Ну, и как вишенка на торте: уважающий себя MISRA'ст должен сразу и с гневом отвергнуть Ардуино, ибо сам факт написания хоть чего-нибудь а среде Ардуино IDE является грубым нарушением целого ряда правил, начиная с глобального правила №3.6 («All libraries used in production code shall be written to comply with the provisions of this document, and shall have been subject to appropriate validation»).

Кстати, последнее надо напоминать страждущим что-то написать на Ардуино для автомобиля :-)

GarryC
Offline
Зарегистрирован: 08.08.2016

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

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