Sobrecarga del operador &&

Hola chicos!

A raíz de un post reciente me he planteado escribir una clase para manejar entradas digitales.

Básicamente se trata de un objeto que encapsula un pin y lee el estado (LOW, HIGH, FALLING o RISING) y usa un debounce para evitar rebotes en la entrada. También se puede usar lógica invertida, que es útil para cuando manejamos interruptores llevados a GND y usamos la resistencia PULLUP, donde
el interruptor cerrado tiene un valor LOW pero la cabeza te dice que debe ser HIGH.

También tiene sobrecarga de operadores de igualdad, AND y OR.

El código de la librería es el siguiente:

  • Archivo de cabecera input.h
#ifndef _INPUT_H_
#define _INPUT_H_

#include <Arduino.h>

// Comentar línea para no usar lógica invertida.
#define USE_INVERTED_LOGIC
// Comentar línea para no usar sobrecarga de métodos.
#define USE_OVERLOADED_METHODS

#ifndef ON
#define ON 1
#endif

#ifndef OFF
#define OFF 0
#endif

class input {
  private:
    uint8_t  pin;
    uint8_t  value;
    uint8_t  prev;
    uint8_t  state;
    uint32_t debounce;
    uint32_t timer;
  public:
    input(uint8_t _pin);
    void update();
    uint8_t  read();
    void setDebounce(uint32_t _debouncd);
    #ifdef USE_OVERLOADED_METHODS
      bool operator==(const uint8_t v);
      bool operator!=(const uint8_t v);
      bool operator!();
      bool operator&&(const input &a);
      bool operator&&(const int &a);
      bool operator||(const input &a);
      bool operator||(const int &a);
   #endif
};

#endif
  • Archivo de implementación input.cpp
#include "input.h"

input::input(uint8_t _pin) {
  pin = _pin;
  pinMode(pin, INPUT_PULLUP);
  value=digitalRead(pin);
  prev = value;
  state = value;
  debounce = 50;
}
void input::update() {
  if ( digitalRead(pin)==value ) {
    timer=0;
  }
  else
  if ( timer==0 ) {
    timer=millis();
  }
  else
  if ( millis()-timer>debounce ) {
    value=!value;
  }
  #ifdef USE_INVERTED_LOGIC
    if ( prev==HIGH && value==LOW  ) state=RISING;
    if ( prev==HIGH && value==HIGH ) state=LOW;
    if ( prev==LOW  && value==HIGH ) state=FALLING;
    if ( prev==LOW  && value==LOW  ) state=HIGH;
  #else
    if ( prev==HIGH && value==LOW  ) state=FALLING;
    if ( prev==HIGH && value==HIGH ) state=HIGH;
    if ( prev==LOW  && value==HIGH ) state=RISING;
    if ( prev==LOW  && value==LOW  ) state=LOW;
  #endif
  prev = value;
  
}

uint8_t input::read() {
  return state;
}

void input::setDebounce(uint32_t _debounce) {
  debounce = _debounce;
}


#ifdef USE_OVERLOADED_METHODS

bool input::operator==(const uint8_t v) {
  return state==v;
}

bool input::operator!=(const uint8_t v) {
  return state!=v;
}

bool input::operator!() {
  if ( state==FALLING ) return false;
  if ( state==RISING ) return  true;
  return !state;
}


bool input::operator&&(const input &a) {
  uint8_t x, y;
  x = state;
  y = a.state;
  (x==FALLING||x==LOW)?x=0:x=1;
  (y==FALLING||y==LOW)?y=0:y=1;
  return x&&y;
}

bool input::operator&&(const int &a) {
  uint8_t x, y;
  x = state;
  a > 3 ? y = 1 : y = a;
  (x==FALLING||x==LOW)?x=0:x=1;
  (y==FALLING||y==LOW)?y=0:y=1;
  return x&&y;
}

bool input::operator||(const input &a) {
  uint8_t x, y;
  x = state;
  y = a.state;
  (x==FALLING||x==LOW)?x=0:x=1;
  (y==FALLING||y==LOW)?y=0:y=1;
  return x||y;
}

bool input::operator||(const int &a) {
  uint8_t x, y;
  x = state;
  a > 3 ? y = 1 : y = a;
  (x==FALLING||x==LOW)?x=0:x=1;
  (y==FALLING||y==LOW)?y=0:y=1;
  return x||y;
}

#endif

Y de momento funciona, pero me he encontrado con un "problemilla", creo que me estoy oxidando como programador.

El caso es que la sobrecarga funciona, pero con limitaciones. Por ejemplo si quiero hacer el AND de tres entradas según como lo haga me da error de compilación. Ejemplo:

#include "input.h"

input i1(2);
input i2(3);
input i3(4);


void setup() {
  pinMode(5, OUTPUT);
}

void loop() {
  i1.update();
  i2.update();
  i3.update();
  // Esto funciona:
  if ( i1 && (i2 && i3)  ) 
    digitalWrite(5,HIGH);
  else 
    digitalWrite(5,LOW);
}

En el código anterior hago AND de las tres entradas agrupando por paréntesis y funciona, pero si quitas el paréntesis:

#include "input.h"

input i1(2);
input i2(3);
input i3(4);


void setup() {
  pinMode(5, OUTPUT);
}

void loop() {
  i1.update();
  i2.update();
  i3.update();
  // Esto NO funciona:
  if ( i1 && i2 && i3  ) 
    digitalWrite(5,HIGH);
  else 
    digitalWrite(5,LOW);
}

Tira error de compilación:

D:\Programas\arduino-1.8.19\portable\sketchbook\pruebaInput\pruebaInput.ino: In function 'void loop()':
pruebaInput:17:17: error: no match for 'operator&&' (operand types are 'bool' and 'input')
   if ( i1 && i2 && i3  )
        ~~~~~~~~~^~~~~
D:\Programas\arduino-1.8.19\portable\sketchbook\pruebaInput\pruebaInput.ino:17:17: note: candidate: operator&&(bool, bool) <built-in>
D:\Programas\arduino-1.8.19\portable\sketchbook\pruebaInput\pruebaInput.ino:17:17: note:   no known conversion for argument 2 from 'input' to 'bool'
exit status 1
no match for 'operator&&' (operand types are 'bool' and 'input')

¿Soy yo que no sé sobrecargar los métodos && y ||, o simplemente, es que el compilador no permite realizar dicha operación con métodos
sobrecargados?

Los tipos bool e int no son sinónimos. Sin embargo, los usas como si fueran lo mismo. Describe otra opción para recargar operador &&, donde uno de los operandos es de tipo bool, y luego puedes usarlo sin paréntesis.
O utilice paréntesis, en cuyo caso la conversión de bool a int se produce automáticamente

He intentado la sobrecarga usando el operador bool, que a efectos practicos sigue siendo un byte de datos y he obtenido el mismo resultado.

bool input::operator&&(const bool &a) {
  bool x;
  (state==LOW || state==FALLING) ? x = false : x = true;
  return x && a;
}

Pero investigando he encontrado esto en Simplify C++ - Operator Overloading: Common Practice.

Logical and, or. These operators exist for built in types only for boolean arguments and are implemented as short circuit operators. That means that the second argument is not evaluated if the first argument already determines the outcome. If you overload the logical operators for user defined types, the short circuit evaluation will not be used, i.e. both operands will always be evaluated. For that reason it is uncommon to overload these operators, at least for the usual boolean semantics.

Lo cual significa, de forma simplificada, que se la sobrecarga de este operador se realiza tan solo de dos en dos, por eso cuando pongo el parentesis funciona, ya que evalua los dos de dentro y luego el tercer &&. Sin embargo, al usar tres variables no reconoce el primer && porque no reconoce el segundo &&.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.