Использование Serial в качестве аргумента булевой функции
Следующая программа компилируется в среде Arduino IDE.
void doSomething(bool) {}
void setup() {
doSomething(Serial);
}
void loop() {}
Но, как и ожидалось, в этом случае этого нет:
class MyClass {};
void doSomething(bool) {}
void setup() {
MyClass myClass;
doSomething(myClass);
}
void loop() {}
Компилятор возвращает ошибку невозможно преобразовать 'MyClass' в 'bool' для аргумента '1' в 'void doSomething(bool)'
Почему возможно скомпилировать первый вариант? Разве Serial не является обычным экземпляром класса (HardwareSerial)? Имеет ли смысл интерпретировать Serial как тип bool?
@noearchimede, 👍-1
Обсуждение2 ответа
Лучший ответ:
HardwareSerial определяет оператор bool(), что позволяет использовать его в логическом контексте.
Классы определяют, можно ли и как преобразовать их экземпляры в тип bool, а также что означает полученный тип bool.
В конкретном случае HardwareSerial это означает, что последовательный порт готов: «Это вернет false только в том случае, если запрос на последовательное соединение USB CDC Leonardo будет сделан до того, как оно будет готово». См. документацию <code>if(Serial)</code>.
И каково назначение оператора bool()?, @noearchimede
Классы определяют, можно ли преобразовать их экземпляры в тип bool и как это значение, а также что оно означает. В конкретном случае HardwareSerial это означает готовность последовательного порта: «Этот код вернёт false только при запросе последовательного соединения USB CDC Леонардо до его готовности». — https://www.arduino.cc/en/Serial/IfSerial, @deltab
Добавляя к ответу, вы можете проделывать подобные чудеса с помощью своих классов/структур, как показано ниже (ну, ответ уже содержит ссылку на способ, но мне просто захотелось добавить его сюда).
Выполнение пользовательской функции преобразования (как это называется в Cppreference здесь)
Чтобы определить свой индивидуальный состав, просто создайте такого участника
operator <desired-type-here> ()
Пример: предположим, у нас есть класс Integer, который мы хотим использовать как логическое значение в операторе if, например, if(x):
class Integer {
public:
Integer(int x) : val{x} {}
int val;
};
void setup() {
Serial.begin(9600);
}
// смотрите здесь
void loop() {
Integer x(42);
if (x) { // Эта строка, очевидно, выдаст ошибку
Serial.println("I swear I forgot what 42 referenced, darn it.");
}
else {
Serial.println("0 is a very powerful number");
}
}
Теперь давайте реализуем наше пользовательское приведение в функции следующим образом:
class Integer {
public:
Integer(int x) : val{x} {}
// здесь произойдет волшебство
// ПРИМЕЧАНИЕ: «const» здесь необязателен. Вы можете указать его, если хотите.
// разрешить использование с "const Integer"
operator bool() const {
return (val != 0);
}
int val;
};
void setup() {
Serial.begin(9600);
}
// смотрите здесь
void loop() {
Integer x(42);
if (x) { // теперь это работает и выведет истинное утверждение.
Serial.println("I swear I forgot what 42 referenced, darn it.");
}
else {
Serial.println("0 is a very powerful number");
}
bool yes = x; // это утверждение теперь также верно, поскольку "x" приводится к bool, а результат присваивается "yes"
}
Дополнительно: Вы также можете сделать так, чтобы для использования функции преобразования требовалось явное приведение типа, добавив explicit к объявлению функции следующим образом:
explicit operator <desired-type>()
Допустим, вы хотите, чтобы ваш класс Integer приводился к типу float, но при этом вы хотите явно указать момент приведения. Поэтому мы изменим наш класс следующим образом:
class Integer {
public:
Integer(int x) : val{x} {}
// Мы... прокомментируем это здесь, потому что компилятор попытается привести
// Преобразовать целое число в число с плавающей точкой, сначала приведя его к типу bool, а затем преобразовав этот тип bool
// к числу с плавающей точкой. Вы также можете указать это явно, чтобы этого не произошло (но
// помните, что вам нужно будет явно приводить типы)
/*operator bool() const {
return (val != 0);
}*/
// Давайте сделаем здесь больше магии
explicit operator float() const {
return static_cast<float>(val); // "(float)val" также работает, но чем уродливее синтаксис приведения, тем легче его найти в коде, что, по-видимому, считается лучшим вариантом.
}
int val;
};
И теперь мы можем сделать это в нашем коде:
void loop() {
Integer x(42);
float y = x; // Компилятор оценит ваши прошлые действия по этому вопросу.
float z = static_cast<float>(x); // Компилятор с радостью это примет.
}
- Как разделить входящую строку?
- Как вывести несколько переменных в строке?
- В чем разница между Serial.write и Serial.print? И когда они используются?
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Программы построения последовательных данных
- Как узнать частоту дискретизации?
- Что такое Serial.begin(9600)?
- Очистить существующий массив при получении новой последовательной команды
Вы не смотрели документацию, да? Поэтому я и проголосовал против., @the busybee
@thebusybee Не обращайте внимания на минусы, но я не согласен с вашими доводами (и с несколько высокомерным риторическим вопросом). Я считаю, что мир Arduino (включая сайт StackExchange) — отличное место для изучения основ программирования, а умение ориентироваться в документации и находить решение таких вопросов, как этот, — это навык, который нужно освоить, поскольку для новичка это неочевидно., @noearchimede
Что ж, как и в любом другом деле, включая непрограммистское ПО, первым делом всегда обращаемся к **предоставленной документации**, а не к случайному поисковику, сайту StackExchange или даже ChatGPT. Однако я каждый день замечаю, что этот навык угасает. Всего наилучшего и удачи!, @the busybee