Вчера на амперке всплыла тема про тернарный оператор. ТС ничтоже сумнящеся заявил, что
1 | sig = <условие> ? <выражение 1> : <выражение 2>; |
это одно и то же.
Подобного рода заявления встречались и у нас. Я обычно шутил насчёт того, что тернарный оператор открывает широкие возможности для настоящих мужчин (не зря его мисрасты не любят) и на этом ограничивался. А тут вдруг по дискуссии на амперке понял, что многие и вправду не понимают моих шуток и считают, что вышеприведённые конструкции действительно идентичны. Наверное, каждый из вас в своей профессии иногда с удивлением ловил себя на мысли, что оказывается «есть совсем простые вещи, которых неспециалисты не знают»! Вот и я себя поймал на этом. Захотелось исправить ситуацию и рассказать про тернарный оператор. Кто всё это знает – от них не убудет, но уверен, что многие не знают или знают не всё.
Сразу скажу, что приведённые выше куски кода не эквивалентны. Они эквивалентны лишь в частном случае, когда типы <выражения 1> и <выражения 2> в точности совпадают. Если же типы <выражения 1> и <выражения 2> различны, то ... там как раз открывается простор для творчества. Можно такого наговноко... (простите) натрюкачить, что хрен потом эту ошибку найдёшь!
Изложение построим так. Сначала я кратко поясняю, что происходит, когда эти выражения имеют различные типы и покажу два безобидных примера, демонстрирующих сказанное. Затем, покажу третий пример, в котором из-за этих особенностей программа просто не компилируется. Потом перейдём к более «вонючим» вещам. Четвёртый пример позволяет подобраться вплотную к болоту и ощутить его запах, а уж в пятом примере мы окунёмся в это болото с головой. Ну, и в конце я скажу пару слов «в защиту детёныша».
Итак, если типы <выражения 1> и <выражения 2> различны, компилятор пытается подобрать ближайший общий тип к которому можно преобразовать и то, и другое. Тип он подбирает по общим правилам преобразования типов (несколько страниц в стандарте), а если не может подобрать общий тип, то ругается и прекращает компиляцию.
Давайте посмотрим как это работает.
Пример №1
06 | inline bool cTrue( void ) { return digitalRead(2) <= 1; } |
07 | inline bool cFalse( void ) { return digitalRead(2) > 1; } |
15 | Serial .println( "При использовании if" ); |
16 | if (cTrue()) Serial .print(c); else Serial .print(b); |
18 | if (cFalse()) Serial .println(c); else Serial .println(b); |
20 | Serial .println( "При использовании ?" ); |
21 | Serial .print( cTrue() ? c : b ); |
23 | Serial .println( cFalse() ? c : b ); |
27 | //////////////////////////// |
Здесь <выражение 1> и <выражение 2> имеют типы char и byte соответственно. Serial.print понимает и то, и другое, но печатает их по-разному - char - как символ, а byte - как число. Обратите внимание, что в варианте с if всё именно так и напечатано. А в варианте с ? оба значение напечатаны как числа, т.е. компилятор привёл из оба к типу byte, как к более общему. Т.е. мы уже видим, что программа ведёт себя по-разному при использовании if и при использовании ?.
Пример №2
06 | inline bool cTrue( void ) { return digitalRead(2) <= 1; } |
07 | inline bool cFalse( void ) { return digitalRead(2) > 1; } |
15 | Serial .println( "При использовании if" ); |
16 | if (cTrue()) Serial .print(i); else Serial .print(d); |
18 | if (cFalse()) Serial .println(i); else Serial .println(d); |
20 | Serial .println( "При использовании ?" ); |
21 | Serial .print( cTrue() ? i : d ); |
23 | Serial .println( cFalse() ? i : d ); |
27 | //////////////////////////// |
Приблизительно тоже самое, только с типами int и double. При использовании if они так и печатаются как int и double, а вот при использовании ? - они оба печатаются как double, т.е. компилятор привёл оба значения к типу double.
Иногда бывает так, что сами по себе <выражение 1> и <выражение 2> оба вполне себе подходят для конечного результата, но не могут быть преобразованы к общему типу. В этом случае имеем ошибку компиляции, хотя «эквивалентная» конструкция с if вполне работает. Об этом третий пример:
12 | Serial .println( "При использовании if" ); |
13 | if (res) Serial .println(res); else Serial .println( "Решение не найдено" ); |
15 | Serial .println( "При использовании ?" ); |
16 | Serial .print( res ? res : "Решение не найдено" ); |
Ну, что , компилятор не нашёл такого общего типа, к которому он мог бы преобразовать и const char * и int. Потому он обругал нас и сказал "сами разбирайтесь". Хотя, если бы он ничего не преобразовывал, то print-то вполне был готов скушать хоть const char *, хоть int, что мы и видим в варианте с if - там всё отлично работает.
Для тех, кому уже не терпится спросить: «И что? Это и всё веселье?» пора сказать – нет господа! Веселье ещё и не начиналось! Веселье начинается когда мы начинаем использовать свои собственные типы данных, а не простейшие встроенные типы, как мы делали до сих пор.
Наш четвёртый пример мало отличается от предыдущих, но в нём мы определим свой тип – комплексное число и попробуем в if и в ? напечатать либо его, либо обычное число с плавающей точкой.
06 | inline bool cTrue( void ) { return digitalRead(2) <= 1; } |
07 | inline bool cFalse( void ) { return digitalRead(2) > 1; } |
09 | class Complex : public Printable { |
13 | Complex( const double & r, const double i = 0) { real = r; imaginary = i; } |
14 | size_t printTo(Print& p) const { |
15 | size_t res = p.print( '(' ); |
18 | res += p.print(imaginary); |
30 | Serial .println( "При использовании if" ); |
31 | if (cTrue()) Serial .print(c); else Serial .print(d); |
33 | if (cFalse()) Serial .println(c); else Serial .println(d); |
35 | Serial .println( "При использовании ?" ); |
36 | Serial .print( cTrue() ? c : d ); |
38 | Serial .println( cFalse() ? c : d ); |
42 | //////////////////////////// |
Вроде, ничего особенного. В случае ? оба числа напечатались как комплексные, что вполне ожидаемо после первых двух примеров. Но тут есть нюанс! Чтобы преобразовать число в комплексное был вызван конструктор класса! А конструктор – это же уже программа! У неё могут быть побочные эффекты и вообще, там много чего может быть.
Вот давайте и посмотрим на более продвинутый пример с конструктором. В пятом примере мы пытаемся создать экземпляр класса String. Как дисциплинированные люди, мы проверяем создался ли экземпляр или для него памяти не хватило. Если создался, то печатаем его, а если не создался, то печатаем сообщение об ошибке. Сообщение – обычная строка – массив символов. Для имитации того, что нам не хватает памяти, мы специально загадим её настолько, чтобы строка уже не создалась и посмотрим, что получится. Итак, пример №5
05 | static inline size_t freeMemory( void ) { |
06 | extern char *__brkval; |
07 | return (__malloc_heap_end ? (size_t)__malloc_heap_end : SP - __malloc_margin) - |
08 | (size_t)(__brkval ? __brkval : __malloc_heap_start); |
15 | const size_t memSize = freeMemory() - 10; |
16 | memset(malloc(memSize),0 , memSize); |
20 | String s = String(M_PI, 6); |
22 | Serial .println( "При использовании if" ); |
23 | if (s) Serial .println(s); else Serial .println( "No memory for string" ); |
25 | Serial .println( "При использовании ?" ); |
27 | Serial .println( s ? s : "No memory for string" ); |
32 | //////////////////////////// |
Как видите с if всё работает! А вот с ? программа зависла! Почему? Ну, понятно, в случае с ? компилятор стал преобразовывать оба выражения тернарного оператора к общему типу. Таковым общим типом является String. Вот он и попытался создать String из нашего сообщения об ошибке! Но ведь на создание String у нас уже нет памяти! Здрасьте, приехали – подвисли :-(
Ну, как? Весело искать такую ошибку, особенно если она не в рафинированном примере,сделанном специально для её демонстрации, а в коде на пару тысяч строк?
И это "мы только зашли туда, чтобы потом всем говорить, что побывали в Лабиринте"! Там чем дальше в лес, тем толще партизаны! А какой простор для творчества предоставляет использование тернарного оператора с автоматическими типами? А с лямбдами? Там такие возможности говнокодерства открываются - куда там жалкому goto! По творческому потенциалу тернарный оператор можно сравнить разве что с #define.
В общем, тернарный оператор - вещь совсем «не для девочек». Ну, а поскольку программисты - в основном девочки (братья Стругацкие врать не станут), то, например MISRA попросту запретила использование этого оператора наравне с тем же goto.
Теперь обещанная пара слов «в защиту детёныша».
В простых случаях типа чисел одного типа, тернарный оператор выглядит очень компактно и понятно. Также есть достаточно мест, где применим только он и никакими if'ами его не заменишь. Это и условная инициализация ссылок, и условная инициализация членов класса в заголовке конструктора и другое.
Дополнение по результата обсуждения
Мне справедливо указали на отсутствие примера, показывающего работа именно с оператором присваивания (с чего начиналась статья).
Вот пример:
04 | struct Quotation : public Printable { |
06 | Quotation( void ) { m_text = "<empty>" ; } |
07 | Quotation( const Quotation & s) { m_text = s.m_text; } |
08 | size_t printTo(Print& p) const { return p.print(m_text); } |
12 | struct Bible : public Quotation{ |
13 | Bible( const char * s = "" ) { m_text = s; } |
20 | struct Fenya : public Quotation { |
22 | Fenya( const char * t, const char * c) : m_canonic(c) { m_text = t; } |
23 | operator Bible( void ) { return Bible(m_canonic.begin()); } |
26 | Bible bq( "\"Идущий за мной сильнее меня\" (Мат. 3:11)" ); |
28 | Fenya fq( "\"Всякому взбрыкнувшему, да отбрыкнется, ибо нех!\" (Мк 4:24)" , |
29 | "\"Какой мерой вы мерите, той и Бог вам отмерит и прибавит еще\" (Мк 4:24)" ); |
35 | inline bool cTrue( void ) { return digitalRead(2) <= 1; } |
36 | inline bool cFalse( void ) { return digitalRead(2) > 1; } |
42 | Quotation q2 = cTrue() ? bq : fq; |
43 | Quotation q3 = cFalse() ? bq : fq; |
44 | Serial .println( "При использовании ?" ); |
50 | if (cTrue()) q0 = bq; else q0 = fq; |
51 | if (cFalse()) q1 = bq; else q1 = fq; |
52 | Serial .println( "При использовании if" ); |
58 | //////////////////////////// |
Результаты с «if» и с «?», как видите, разные.
Но тут мне хотелось бы сделать дополнительные пояснения об операторе присваивания и ещё об одних граблях.
При выполнении оператора присваивания, сначала вычисляется его правая часть. И только потом левая. В нашем случае это означает, что операции преобразования <выражения 1> и <выражения 2> к единому типу будут происходить так, как было описано выше, без оглядки на тип выражения в левой части оператора присваивания! И только потом, будет сделана попытка привести получившийся в правой части «общий» тип к типу левой части.
Отсюда следует, что в принципе возможна ситуация, когда сами по себе <выражение 1> и <выражение 2> вполне могли бы быть приведены к типу левой части, а вот тот «общий» тип, к которому их преобразовали – уже не приводится. В этом случае получим ошибку компиляции.
Евгений Петрович, при всем уважении, крайне разочарован. (((((((((((((
Вначале Вы объявили, что две конструкции не являются эквивалентными, но ни одного подтверждающего это утверждения примера не привели.
Имманентным атрибутом каждого из двух исходных выражений является оператор присваивания (sig = ), но ни в одном из пяти последующих примеров оператор присваивания не используется.
Другими словами, все это очень интересно (как и любой публикуемый Вами материал), но в данном случае, увы, иллюстрация исходного утверждения (о неэквивалентности конструкций, содержащих оператор присваивания) не приведено.
1
sig = <условие> ? <выражение 1> : <выражение 2>;
2
3
// И
4
5
if
(<условие>) {
6
sig = <выражение 1>;
7
}
else
{
8
sig = <выражение 2>;
Соответствует:
sig = cTrue() ?
Serial
.print(c)
:Serial
.print(b)
;if
(cTrue())
sig =Serial
.print(c);
else
sig =
Serial
.print(b);
А примеры объясняют несколько иное.
Странно, что Вы не увидели что там совершенно неважно присваивание или нет, а примеры сразу с печатью - исключительно для укорочения кода.
крайне разочарован. (((((((((((((
Вам нужен пример с присваиванием?
А примеры объясняют несколько иное.
А примеры объясняют несколько иное.
Одновременно оба утверждения быть ложными не могут. Или вы делаете логическое И ?
Увидел
return
digitalRead(2) <= 1;
И дальше смотреть не стал. Магические числа какие-то... Это ж кто так учил проверять результат digitalRead(2)? Архат может?
Смотри в доку!
Функция digitalRead()
digitalRead()
Описание
Функция считывает значение с заданного входа - HIGH или LOW.
А примеры объясняют несколько иное.
Женя! Я слышал историю про бисер и домашних животных. Что-то там не очень выходило. ;)
Я завидую твоему терпению, я бы уже начал матюгами щедро посыпать, а потом сердечное ведрами глотать.
Одновременно оба утверждения быть ложными не могут. Или вы делаете логическое И ?
Ну, почему же не могут. Вы утверждали
1) что конструкции с if и с ? из первого поста эквивалентны - это ложно ибо они не эквиваленты
2) что мой текст демонстрирует другое - это тоже ложно ибо он именно это и демонстрирует.
Вы, что мне интересно.
Вот Сергей наехал на мою манеру изложения, на то, что я плохо подобрал иллюстрации и доказательства своей главной мысли. Он не в первый раз это делает. Я обычно как-то отгавкиваюсь (как в #3), но прислушиваюсь к нему потому, что обычно в его словах что-то есть :-) В правильности же самой главной мысли он не сомневался потому, что понимает, что она верная.
Вы же попытались объяснить мне, что главная мысль неверна (конструкции эквивалентны), а мои примеры совсем не про неё, а про что-то другое. Вот ответьте мне, Вы правда считаете, что я не понимаю о чём пишу? Или Вы решили меня потроллить, чтобы на слабо заставить сделать пример с присваиванием? Ответьте пожалуйста, а я Вам тогда приз выдам :-)
Будет нормальный разговор, объясню ещё раз, покажу пример именно с присваиванием - их есть у меня, только я не стал приводить, т.к. он посложнее этих. Но вот andriano наехал, что присваивания нет :-( В какой-то мере он, наверное, прав. То, что Upper вообще ничего не понял и утверждает, что конструкции эквивалентны, как раз доказывает правоту andriano. Так я покажу и с голимым присваиванием, и объясню ещё раз (или andriano попрошу объяснить) мне не жалко.
Ну, а не будет нормального разговора - ты знаешь, как я поступаю ( и тебе так советую). Коротко, одноразово и никаких сердечных. А то и вообще молча, как с постом #6.
Upper,
ну если так, так почему же просто не попросить привести пример именно с присваиванием? Зачем Вы в посте №2 умничать начали про "несколько иное"?
Вот Вам чистый пример с присваиванием.
01
// Пример граблей оператора ? №6
02
03
// Класс Цитата
04
struct
Quotation :
public
Printable {
05
String m_text;
06
Quotation(
void
) { m_text =
"<empty>"
; }
07
Quotation(
const
Quotation & s) { m_text = s.m_text; }
08
size_t printTo(Print& p)
const
{
return
p.print(m_text); }
09
};
10
11
// Класс "Библия", наследник "Цитата"
12
struct
Bible :
public
Quotation{
13
Bible(
const
char
* s =
""
) { m_text = s; }
14
};
15
16
// Класс "Феня", хранит цитату в двух видах.
17
// В каноническом и на фене. Везде использует
18
// вариант на фене, но при преобразовании в
19
// Bible выдаёт каноническую цитату, а то неприлично
20
struct
Fenya :
public
Quotation {
21
String m_canonic;
22
Fenya(
const
char
* t,
const
char
* c) : m_canonic(c) { m_text = t; }
23
operator
Bible(
void
) {
return
Bible(m_canonic.begin()); }
24
};
25
26
Bible bq(
"\"Идущий за мной сильнее меня\" (Мат. 3:11)"
);
27
28
Fenya fq(
"\"Всякому взбрыкнувшему, да отбрыкнется, ибо нех!\" (Мк 4:24)"
,
29
"\"Какой мерой вы мерите, той и Бог вам отмерит и прибавит еще\" (Мк 4:24)"
);
30
31
//
32
// Две функции ниже всегда возвращают true и false сщщтветственно
33
// Но неочевидным для компилятора образом, чтобы он выбросил
34
// проверку при оптимизации
35
inline
bool
cTrue(
void
) {
return
digitalRead(2) <= 1; }
36
inline
bool
cFalse(
void
) {
return
digitalRead(2) > 1; }
37
38
//
39
void
setup
() {
40
Serial
.begin(9600);
41
42
Quotation q2 = cTrue() ? bq : fq;
43
Quotation q3 = cFalse() ? bq : fq;
44
Serial
.println(
"При использовании ?"
);
45
Serial
.println(q2);
46
Serial
.println(q3);
47
Serial
.println();
48
//
49
Quotation q0, q1;
50
if
(cTrue()) q0 = bq;
else
q0 = fq;
51
if
(cFalse()) q1 = bq;
else
q1 = fq;
52
Serial
.println(
"При использовании if"
);
53
Serial
.println(q0);
54
Serial
.println(q1);
55
}
56
void
loop
(
void
) {}
57
58
////////////////////////////
59
//
60
// РЕЗУЛЬТАТ
61
//
62
// При использовании ?
63
// "Идущий за мной сильнее меня" (Мат. 3:11)
64
// "Какой мерой вы мерите, той и Бог вам отмерит и прибавит еще" (Мк 4:24)
65
//
66
// При использовании if
67
// "Идущий за мной сильнее меня" (Мат. 3:11)
68
// "Всякому взбрыкнувшему, да отбрыкнется, ибо нех!" (Мк 4:24)
69
//
Как видите, результаты получаются разные. Почему? Это написано в стартовом посте - он именно об этом, а вовсе не "несколько иное".
Изучайте разбирайтесь. А если непонятно - задавайте вопросы, а не ...
Я думаю самым лучшим доказательством не эквивалентности конструкций должен стать дизассемблированный код. Эквивалентные конструкции должны создать эквивалентный код. Очевидно, что компилятор использует различные шаблоны для этих конструкций.
И тут кроме как "Спасибо дядя Женя !", сказать нечего.
Красиво!
Еще раз убедился, что я не знаю Си++.
Вот в строке 23 используется метод begin() переменной типа String. А в документации по типу String я такого метода не нахожу: https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/
И почему, скажем, не m_canonic.c_str()?
Попкорн на исходе, но с удовольствием слежу!..))
Я бы сказал они были идентичны до того как Си перерос в С++.
Сереж! Прости плз, но вечер, литр самогона и все эти вещи. Это снова наезд или тебе лень открыть WString.h в коре ардуино ИДЕ?
Б..же! Где Женя берет столько терпения? Может пить нужно меньше? ...или больше? Но что-то делать точно надо!
Я бы сказал они были идентичны до того как Си перерос в С++.
..ля! Нет, это не так. Тернарный оператор ВЫНУЖДАЕТ компилятор приводить к общему типу альтернативы, а if() - нет. Вот и вся разница, из которой много чего можно получить.
Есть еще куча неочевидных особенностей компиляции.
Я бы сказал они были идентичны до того как Си перерос в С++.
..ля! Нет, это не так. Тернарный оператор ВЫНУЖДАЕТ компилятор приводить к общему типу альтернативы, а if() - нет. Вот и вся разница, из которой много чего можно получить.
Есть еще куча неочевидных особенностей компиляции.
А если справа от "?" одинаковые типы данных?
Я бы добавил, На Си в процедуре присвоения всегда должны были использоваться одни типы данных. Отсутствие указания типа, при неявном преобразовании могло внести ошибку, поэтому всегда использовалось явное указание.
Я думаю самым лучшим доказательством не эквивалентности конструкций должен стать дизассемблированный код. Эквивалентные конструкции должны создать эквивалентный код. Очевидно, что компилятор использует различные шаблоны для этих конструкций.
И тут кроме как "Спасибо дядя Женя !", сказать нечего.
Тогда писали свою ОС, я писал библиотеки Си для нашей ОС.
Это снова наезд или тебе лень открыть WString.h в коре ардуино ИДЕ?
В коре или в корне?
У меня в папке Ардуино 9 штук WString.h, причем ни одного из них - в корне. А что такое "в коре", я вообще не знаю.
И потом, зачем открывать какой-то файл (кстати, откуда я могу узнать, какой именно файл нужно открывать, если у меня в текущей версии этих файлов 21603 штуки?), если есть официальная документация?
Опять же, даже открыв указанный файл, я не понял, в чем отличие begin() от c_str(), т.к. не понял, для чего нужны одновременно buffer и wbuffer, не говоря о том, почему в данном случае нужен именно wbuffer.
PS. И, кстати, убедился, что "наезд" был обоснованным: из пяти примеров исходного поста темы никак простым способом не следует шестой, в котором используется присваивание.
MISRA попросту запретила использование этого оператора наравне с тем же goto.
поддерживаю МИСРУ в обоих перечисленных случаях :)
Евгений, спасибо!
пятница, слежу, продолжение будет?
пятница, слежу, продолжение будет?
Я так понимаю, доклад окончен, и идет процесс ответов на вопросы.
Хотите продолжения - задавайте вопрос. Попросите, например, привести пример с присваиванием на Си.
продолжение будет?
В какой-то мере продолжение.
И, да, забыл ответить про Си.
Там всё гораздо менее творчески :-( Вот что написано в ISO/IEC 9899:2018, § 6.5.15.
Там, конечно, можно поиграть с квалификаторами (когда, например, ни с того ни с сего появляется const или volatile), но это очень тонкие вещи и эффектные примеры на них трудно сделать.
Евгению - спасибо.
Но в моем понимании главной проблемой на которую Евгений не акценитровал внимание новичков - это то, что здесь вопрос не в конструкциях а в контексте применения данных конструкция. Примеров можно приводить много (и из других операторов), но все это сводится к простому правилу:
надо писать просто и единообразно, уж если используем процедурный язык (а с++ это именно процедурный язык) то в нем не стоит использовать подходы из функциональных языков, и все эти "замыкания", и прочие спагетины как в условиях так и в любых других местах применения это вред...
Операторы должны быть простыми.... Особенно для новичков...
зы
если чего, я несколько лет программировал на AutiLisp, и знаю толк в извращениях :)
Хотите продолжения - задавайте вопрос. Попросите, например, привести пример с присваиванием на Си.
Пример ЧИСТО с присваиванием. Женя, позволишь?
Наслаждаемся. Я специально сделал случайные числа, чтобы иногда было одинаково, а иногда нет. Запускать на Нано.
Очень "приятно" подобную ошибку искать в релизе ;)))))))).
01
const
int16_t Modulo = 1000;
02
03
template<typename T>
04
T mod (T a, int16_t b) {
05
T x = a % b;
06
if
(x < 0)
return
x + b;
07
return
x;
08
}
09
10
int
Wprintf(
const
char
* __fmt, ...) {
11
char
s[64];
12
va_list ap;
13
va_start(ap, __fmt);
14
vsnprintf(s, 64, __fmt, ap);
15
va_end(ap);
16
Serial
.print(s);
17
}
18
19
void
setup
() {
20
Serial
.begin(115200);
21
}
22
23
void
loop
() {
24
int16_t x = random(Modulo);
25
int16_t y = random(Modulo);
26
27
uint16_t ux = x;
28
uint16_t uy = y;
29
30
int
rIF, rTER;
31
32
if
(x < y) rIF = mod((x - y), Modulo) ;
33
else
rIF = mod((uy - ux), Modulo) ;
34
35
rTER = mod((x < y) ? (x - y) : (uy - ux), Modulo);
36
37
Wprintf (
"if vs ()?():()===> %d %d\n"
, rIF, rTER);
38
39
delay(1000);
40
}
Операторы должны быть простыми.... Особенно для новичков...
В этой фразе есть противоречие. Очень часто новичок не понимает что на самом деле просто, а что сложно.
Женя! Просто как упражнение для ума. Измени ровно ОДНУ букву в моем примере, чтобы глюк пропал. Ну если интересно, конечно.
Граф, ты читер! :-)
Измени ровно ОДНУ букву
Ты, видимо имел в виду не изменить, а добавить. Букву "u" если точнее. В какое место её добавить колоться не буду - не один я тут :-)
Измени ровно ОДНУ букву
Ты, видимо имел в виду не изменить, а добавить. Букву "u" если точнее. В какое место её добавить колоться не буду - не один я тут :-)
Так точно! ;)) Сперва я написал без глюка, и потом пришлось как раз убирать эту "u", чтобы организовать глюк.
Граф, скажи лучше как тебе вот такая фраза из стандарта: Using a bool value in ways described by this document as “undefined”, such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false.
Вот бы пример придумать! Я подумаю, кстати :-)
Измени ровно ОДНУ букву
Ты, видимо имел в виду не изменить, а добавить. Букву "u" если точнее. В какое место её добавить колоться не буду - не один я тут :-)
кстати я недавно попадал, когда наоброт была лишняя эта самая буква u :)
ну что-то типа
void i;
if ((boolean) i) {}
ну что-то типа
void i;
ты точно понимаешь что пишешь?
Граф, скажи лучше как тебе вот такая фраза из стандарта: Using a bool value in ways described by this document as “undefined”, such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false.
Вот бы пример придумать! Я подумаю, кстати :-)
Думаю... пока компилятор мертво стоит на том, чтобы не дать мне даже попробовать использовать не инициализированный автоматический объект. Думаю, как его наипать?
ну что-то типа
void i;
ты точно понимаешь что пишешь?
так лучше? ну нет у меня на работе ничего для проверки синтаксиса :)
void* i;
if (*((boolean*) i)) {}
Наслаждаемся.
Оно, вроде, красиво, но:
1
sketch_apr09a:4: error:
'T'
does not name a type
2
3
T mod (T a, int16_t b) {
4
5
^
6
7
exit status 1
8
'T'
does not name a type
Сережа! Будь внимательнее при копировании. Ты строчку template<> потерял. Это шаблон. Несколько неожиданно для тебя. За неделю уже вторая странность. Первая была с незнанием понятия "конечная точка".
________
ЗЫ: ты вот скажи: если у тебя появилась ошибка при компиляции, тебе не кажется, что нужно проверить себя в первую очередь??? И не позориться. Фу!
Зафиксирую.
Нет, Влад, не потерял я ее, просто компилятор приводит лишь тот фрагмент, в котором ошибка, а не весь код примера.
Для контроля только что скопировал код непосредственно из окна своей IDE:
01
const
int16_t Modulo = 1000;
02
03
template<typename T>
04
T mod (T a, int16_t b) {
05
T x = a % b;
06
if
(x < 0)
return
x + b;
07
return
x;
08
}
09
10
int
Wprintf(
const
char
* __fmt, ...) {
11
char
s[64];
12
va_list ap;
13
va_start(ap, __fmt);
14
vsnprintf(s, 64, __fmt, ap);
15
va_end(ap);
16
Serial
.print(s);
17
}
18
19
void
setup
() {
20
Serial
.begin(115200);
21
}
22
23
void
loop
() {
24
int16_t x = random(Modulo);
25
int16_t y = random(Modulo);
26
27
uint16_t ux = x;
28
uint16_t uy = y;
29
30
int
rIF, rTER;
31
32
if
(x < y) rIF = mod((x - y), Modulo) ;
33
else
rIF = mod((uy - ux), Modulo) ;
34
35
rTER = mod((x < y) ? (x - y) : (uy - ux), Modulo);
36
37
Wprintf (
"if vs ()?():()===> %d %d\n"
, rIF, rTER);
38
39
delay(1000);
40
}
Сережа! Ну проверь себя. Ну пожалуйста!!! Все работает в штатной ИДЕ 1.8.13. Форум попросить помочь скомпилировать??? Ну блин! Я никогда не публикую код, не запустив. Ты просто оскорбляешь меня, прости за резкость.
Я только что вставил себе в ИДЕ то, что ты опубликовал. Всё, естественно собирается. И проверить это может ЛЮБОЙ находящийся на форуме.
Давай попробуем разобраться. Какая выбрана плата? Какая версия ИДЕ? Не менял ли ты настройки у себя? А ты случайно файл не назвал с расширением ".c", потому что в С нет темплейтов?
========================
Вот видео с записью с моего экрана прямо сейчас. Сергей! Твое поведение - недопустимо!!!!!!!!!!!!!!!!!!!!!!!!!!!
https://youtu.be/1L7wh1Vc3Do
Коллеги! Очень прошу тех, у кого есть возможность проверить и отписаться! Для меня это ПРИНЦИПИАЛЬНЫЙ вопрос. Меня Андриано обвинил в подлоге, прошу откомпилировать и запустить код даже не из моего, а из его поста!
Я собирал на Нано, Мега, Леонардо. ESP8266 и ESP32. Для ESP32 нужно добавить return в функцмю wprintf, там по умолчанию жесткая проверка возвращаемых типов. Для остальных плат ничего добавлять не надо.
Везде собирается, на 32-битных нет "глюка", понятно почему, если не понятно, объясню я или Женя позже. Сейчас же мне важно разобраться с обвинениями.
Нано, компилируется, запускается.
01
if
vs ()?():()===> 878 878
02
if
vs ()?():()===> 666 202
03
if
vs ()?():()===> 322 322
04
if
vs ()?():()===> 261 261
05
if
vs ()?():()===> 86 86
06
if
vs ()?():()===> 52 52
07
if
vs ()?():()===> 598 134
08
if
vs ()?():()===> 308 308
09
if
vs ()?():()===> 402 402
10
if
vs ()?():()===> 984 984
11
if
vs ()?():()===> 627 163
12
if
vs ()?():()===> 821 357
13
if
vs ()?():()===> 55 55
14
if
vs ()?():()===> 271 807
15
if
vs ()?():()===> 291 291
16
if
vs ()?():()===> 359 359
17
if
vs ()?():()===> 706 706
18
if
vs ()?():()===> 758 294
19
if
vs ()?():()===> 241 241
20
if
vs ()?():()===> 479 479
21
if
vs ()?():()===> 740 276
22
if
vs ()?():()===> 399 399
23
if
vs ()?():()===> 837 373
24
if
vs ()?():()===> 848 384
25
if
vs ()?():()===> 764 300
Граф, думаю, andriano тебя тебя троллит. Посмотри внимательно на #29 - вот чует моё сердце, что он твой темплэйт на Си компилирует.
Green! Спасибо!
Граф, думаю, andriano тебя тебя троллит. Посмотри внимательно на #29 - вот чует моё сердце, что он твой темплэйт на Си компилирует.
"Андриано" в курсе, что обвинения во лжи моё больное место. Я с 6 утра уже не сплю и глотаю бета-блокаторы!!!!! Если ты прав - так еще хуже! Тогда это просто ..дство! ...слов нет! ...сорри!
С++ темплейт там не принципиален, как любой понимает. Можно просто "%" оставить, будет еще нагляднее. Темплейт - украшательство, исправляющее дурь авторов стандарта, по которой "%" может давать отрицательные значения. (в половине ЯП, а в другой (например в Питоне) математики победили).