Ámbitos y namespaces, despacito, 1ª entrega

Antes que nada quiero decirte que este post está generado como un fichero .ino, el cual te puedes descargar para hacer pruebas de todo lo que voy a indicar, de esa manera no te hace falta ir copiando el código que va a aparecer e ir probando cada uno de estos elementos dentro de un Sketch que tengas que fabricarte, ya está fabricado y lo puedes ver en tu IDE de Arduino.

El scope o ámbito es dónde vive tu función, constante, variable u objeto, y esto se puede modificar o controlar ligeramente.

El ámbito puede ser global o local, pero esto puede ser cambiado.

#include <Arduino.h>
String soyGlobal = "Si, soy una variable global";

Esta variable global la puedes usar a lo largo de todo tu Sketch o programa, como lo quieras llamar, porque está definida para que se use globalmente, y eso ocurre porque no está dentro de nada, está suelta.

Lo mismo ocurre con una función, declararé dos funciones, las cuales voy a llamar 'Tony' y 'Luis', que somos mi hermano y yo. Luego verás por qué nosotros

void Tony(){
    String mensaje = "Hola, soy Tony, el que ha escrito esto";
    Serial.println(mensaje);
}


void Luis(){
    String mensaje = "Hola, soy Luis, el hermano del que ha escrito esto";
    Serial.println(mensaje);
}

Dentro de estas funciones hay dos variables que son locales, su ámbito es local, y por eso, el compilador no da ningún error al traducirlo, porque sabe perfectamente cual es la variable mensaje de 'Tony' y mensaje de 'Luis', conoce su ámbito, ambas viven perfectamente dentro de mi hermano y de mí y no molestan a nadie.

Supongamos que estamos en Requena, el pueblo de mis padres, y resulta que, casualidad de la vida (cosas de pueblos pequeños españoles), mis primos, hijos de un primo de mi hermano, se llaman también Tony y Luis

void Tony(){
    String mensaje = "Hola, soy Tony, el primo del que ha escrito esto";
    Serial.println(mensaje);
}

void Luis(){
    String mensaje = "Hola, soy Luis, el primo del que ha escrito esto";
    Serial.println(mensaje);
}

Te pido que verifiques esto en tu IDE de Arduino y verás que te va a dar varios errores, en concreto te dirá:

FIledebug:52:6: error: redefinition of 'void Tony()'

y unas cuantas cosas más. Lo importante que que te dice REDEFINITION, es decir, te dice que no sabe de qué Tony estás hablando en la línea 52.

Ahora lo que tienes que hacer es tomar las líneas de la 52 a la 62 y poner delante de ellas las dos barras de comentarios

//

No quiero que las borres para que cuando yo diga la línea tal de aquí en adelante, ambos tengamos las mismas líneas, sino este tutorial será un poco más complicado.

Te quedará algo como esto

  //void Tony(){
  //    String mensaje = "Hol
  //    Serial.println(men
  //}
  //
  //
  //
  //void Luis(){
  //    String mensaje = "Hola
  //    Serial.println(m
  //}

Poco más o menos.

En Requena lo tienen Claro, Mi hermano y yo somos "LOS DE LA BELEN", que es el nombre de mi madre, por el contrario mis primos, son "LOS DEL EUSEBIO", que es el nombre de mi tío.

Ya sabemos todos que mi madre es más famosa que mi padre, pero bueno, eso no lo resuelve Arduino.

Sigamos

BELEN y EUSEBIO son los ámbitos a los cuales los hermanos pertenecemos, igual que los apellidos, pero me gusta más este ejemplo de mi pueblo, así que ahora vamos a declarar bien a mis primos, para eso vamos a establecer un nombre de familia, un apellido, que en C++ (Y por ende en Arduino) se llama 'namespace', o espacio de nombres.

namespace LOS_DEL_EUSEBIO {

  void Tony(){
    String mensaje = "Hola, soy Tony, el primo del que ha escrito esto";
    Serial.println(mensaje);
  }

  void Luis(){
    String mensaje = "Hola, soy Luis, el otro primo del que ha escrito esto";
    Serial.println(mensaje);
  }
};

Como podrás comprobar, el compilador ya no da ningún error en la declaración de mis primos, porque sabe a que ámbito pertenece cada Tony y Cada Luis.

Ahora vamos a hacer una función para que todo el pueblo de Requena salude:

void RequenaSaluda(){

    Serial.println("Requena saluda");

    Tony();
    Luis();

    LOS_DEL_EUSEBIO::Tony();
    LOS_DEL_EUSEBIO::Luis();
}

Como verás en las líneas 146 y 147 (por eso te pedí que no las borraras, para tener las mismas líneas ambos), se ha tenido que decir un ámbito adicional, el apellido C++ (namespace para los amigos) y de esa manera, aunque todas las funciones usan la misma variable mensajeadora:

mensaje

no hay duda de cual de todas ellas es la que hay que usar en cada caso.

Esto te permite coger dos trozos de programas de dos programadores diferentes y mezclarlos sin riesgo; imagínate que ambos han definido una variable, constante o objeto que se llama

imprimir

Tendríamos problemas para saber cual de todos tenemos que imprimir.

Vamos a lanzar el sketch y vamos a ver qué pasa

void setup(){
    Serial.begin( 9600 );

    RequenaSaluda();

}
void loop(){
}

RequenaSaluda.ino (5.08 KB)

Vamos a seguir con esto de los namespaces y los scope o ámbitos Como me parece fatal que mi hermano y yo seamos declarados los Tony y Luis universales, voy a declaranos, tanto a mis primos y a nosotros, con el
apellido correcto.

Igual que antes, todo está en el archivo adjunto, tanto código como comentarios para que te lo descargues.

#include <Arduino.h>

namespace LOS_DE_LA_BELEN {

void Tony(){
   String mensaje = "Hola, soy Tony, el que ha escrito esto";
   Serial.println(mensaje);
}
void Luis(){
   String mensaje = "Hola, soy Luis, el hermano del que ha escrito esto";
   Serial.println(mensaje);
}
};

namespace LOS_DEL_EUSEBIO {

 void Tony(){
   String mensaje = "Hola, soy Tony, el primo del que ha escrito esto";
   Serial.println(mensaje);
 }
 void Luis(){
   String mensaje = "Hola, soy Luis, el otro primo del que ha escrito esto";
   Serial.println(mensaje);
 }
};

Si re acuerdas, para que Requena saludase, en la línea 139 del otro Sketch definimos la función donde mi hermano y yo eramos universales, ahora esa función generaría un error de compilación, ahora debe ser definida de otra manera.

void RequenaSaluda(){

   Serial.println("Requena saluda");

   LOS_DE_LA_BELEN::Tony();
   LOS_DE_LA_BELEN::Luis();

   LOS_DEL_EUSEBIO::Tony();
   LOS_DEL_EUSEBIO::Luis();
}

Te invito a que tomes la función 'RequenaSaluda' y la modifiques a como estaba, verás que ya no logras que funcione, sólo de esta manera puede funcionar, pero PUEDE FUNCIONAR.

Si tomas pedazos diferentes de código que debes unir para generar tu Sketch, esto te va a pasar o te puede pasar, no sólo si cortas o pegas, también si usas librerías de terceros, por ejemplo para manejar un motor, un servo, una pantalla TFT.

Te puede pasar que pedazos que sueltos funcionan, juntos no; es una de las cosas que más tiempo ocupa en el tiempo de los moderadores de este foro: decirle a la gente que está haciendo un Frankestein, que no está uniendo código, sino cosíendolo.

Esto ofrece mucho más poder, mira, porque dirás que eso de tener que escribir todo el rato 'LOS_DE_LA_BELEN' o 'LOS_DEL_EUSEBIO' es aburrido, y lo es.

Vamos a definir dos funciones, cada una de las casas van a saludar de forma independiente, sin mezclarse.

void laCasaDeLaBelenSaluda() {
   Serial.println("La casa de la Belén saluda");

   LOS_DE_LA_BELEN::Tony();
   LOS_DE_LA_BELEN::Luis();
}

void laCasaDeLosEusebioSaluda() {
   Serial.println("La casa de la familia del Eusebio saluda");

   LOS_DEL_EUSEBIO::Tony();
   LOS_DEL_EUSEBIO::Luis();
}

Esto es una manera de hacerlo, pero desde luego, mi tía y mi madre se nos van a estar burlando por llamarnos dentro de casa por nombre y apellidos, así que vamos a hacerlo bien.

void laCasaDeLaBelenSaludaBien() {
   using namespace LOS_DE_LA_BELEN;

   Serial.println("La casa de la Belén saluda BIEN");

   Tony();
   Luis();
}

Mira la línea 102, hemos declarado el apellido de la casa, por lo tanto, a partir de ese momento, y en el scope o ámbito de esa función, mientras duren las {}, ya sabe el compidador a que Tony y que Luis tiene que buscar y recuerda una cosa, mi hermano y yo dentro tenemos una variable común
String mensaje

Pero en ámbitos distintos, por eso no se mezclan ni se confunden.

Las llaves están para eso en C y C++, para indicar dónde empieza y donde acaba un ámbito. El ámbito no empieza con el #include, el #include te permite dividir un archivo en pedazos, nada más hace eso, para que sea más fácil de leer o para mezclar más fácilmente pedazos que no nacieron juntos.

Primero voy a hacer que la casa de mi tío salude bien

void laCasaDeLosEusebioSaludaBien() {
   using namespace LOS_DEL_EUSEBIO;

   Serial.println("La casa de la familia del Eusebio saluda BIEN");

   Tony();
   Luis();
}

Esto es importante, porque te permite tomar librerías de terceros y, en caso de que existan colisiones de nombres de objetos, variables, funciones, o cualquier otra cosa, lo puede contener.

Te invito a que juegues con el código y lo cambies y lo hagas dejar de funcionar y hacer funcionar de diferentes maneras para que pruebes lo que te digo.

void setup(){
   Serial.begin( 9600 );

   RequenaSaluda();

   laCasaDeLaBelenSaluda();
   laCasaDeLosEusebioSaluda();


   laCasaDeLaBelenSaludaBien();
   laCasaDeLosEusebioSaludaBien();

}

void loop(){

}

RequenaSaluda2.ino (4.66 KB)