(SOLUCIONADO) Error de compilación en un ejemplo para una librería

¡Hola a todos!

El asunto aquí es (como el título dice) que estaba intentando crear una librería para la IDE de Arduino. Esta ya la tengo terminada, donde me he quedado trabado es a la hora de crear unos ejemplos para la librería. El ejemplo es este:

#include <ByteExtractor.h>
byte array[2];

void setup() {
  Serial.begin(9600);
  while (!Serial) {}

  for (int n = 20; n >= -20; n++) {
    ByteExtractor.getBytesFromInt(n, array);
    PrintArray(n);
    Serial.println();
  }
}

void loop() {
  // Nothing happens in the loop
}

void PrintArray(int number) {
  Serial.print(F("Array's content: "));
  for (byte i = 0; i < sizeof(array); i++) {
    Serial.print(array[i], HEX);
    Serial.print('\t');
  }
  Serial.println();
  Serial.print(F("Decimal number: "));
  Serial.println(number, DEC);
}

La librería la adjunto en un zip (para que fácilmente se pueda “instalar”).

El error de compilación es el siguiente:

C:\Users\[miUsuario]\Documents\Arduino\PrintBytesFromInt\PrintBytesFromInt.ino: In function 'void setup()':

PrintBytesFromInt:9: error: expected unqualified-id before '.' token

     ByteExtractor.getBytesFromInt(n, array);

                  ^

exit status 1
expected unqualified-id before '.' token

Lo que me deja en duda: que tiene de malo usar el nombre de la librería antes del punto?
Digo, por qué no pasa eso en librerías como SD? (funciones: begin, open, exists, remove, mkdir, rmdir)

La idea es evitar instanciar la clase ByteExtractor; no tiene sentido ya que esta no almacena ningún dato, solo contiene funciones.

¡Agradecería bastante que me echen una manita! :smiley:

PD: hasta que acabe y pruebe todos los ejemplos que pienso hacer para esta librería, le hago la respectiva documentación como se debe :wink:

ByteExtractor.zip (1.17 KB)

Digo no deberias crear un contructor de tu objeto?

En tu caso, lo más sencillo a mi parecer sería no utilizar clases, pues si sólo son funciones, ¿para qué tener que llamar a cualquiera de ellas con el prefijo "ByteExtractor.funcion"?

Haces referencia a la librería SD. Esta librería (y muchas otras) utilizan una clase con sus propiedades y métodos, y como están pensadas para utilizar un solo objeto de dicha clase, lo crean y le dan el nombre "amigable".
Por ejemplo, en la librería SD, el objeto SD es una instancia de la clase SDClass.
Si aún quieres utilizar tu librería como objeto (a pesar de no ser necesario, por tratarse sólo de funciones), lo que puedes hacer es lo siguiente:

  • Renombra tu clase de ByteExtractor a ByteExtractorClass. En el fichero ByteExtractor.cpp, por supuesto, también debes renombrar todos los prefijos ByteExtractor:: a ByteExtractorClass::
  • Al final de tu archivo ByteExtractor.h, y fuera de la clase, agrega la siguiente línea:
extern ByteExtractorClass ByteExtractor;

Y por último, al final de tu archivo ByteExtractor.cpp agrega la siguiente línea:

ByteExtractorClass ByteExtractor;

Tu archivo de ejemplo, en principio creo que compilaría tal y como está ahora.

noter:
En tu caso, lo más sencillo a mi parecer sería no utilizar clases, pues si sólo son funciones, ¿para qué tener que llamar a cualquiera de ellas con el prefijo "ByteExtractor.funcion"?

Eso es lo que intento hacer.
Lo que ocurre es que me guié en crear la librería con este tutorial, pero ahí parece que enseñan cómo hacer una "instanciable"; mas no como yo lo pretendo.

noter:
Haces referencia a la librería SD. Esta librería (y muchas otras) utilizan una clase con sus propiedades y métodos, y como están pensadas para utilizar un solo objeto de dicha clase, lo crean y le dan el nombre "amigable".

A ver si entendí. Esto hace que la librería se "auto-instancie" con solo el hecho de incluirla?
Bueno, si eso aun así implica que va a consumir algo de memoria RAM; entonces mejor que el código "herede" esas funciones con solo incluir la librería. Será eso posible?

Digo, así como los .cpp ganan acceso a las funciones de Arduino (pinMode, digitalWrite y esos) con solo incluir esta línea al principio:

#include "Arduino.h"

Digo que las "hereda" porque:

No se instancia:

Arduino arduino; // Esto nunca ocurre

Tampoco se usa el nombre de la librería para llamar a las funciones:

Arduino.pinMode(13, OUTPUT); // Esto tampoco ocurre

Sino que simplemente:

pinMode(13, OUTPUT);
// Esto es válido dentro de un .cpp siempre y cuando la línea #include "Arduino.h" esté presente

Por lo tanto, aquí va la pregunta final: ¿qué debo hacer para que las funciones de una librería se "hereden" al código, y con solo el hecho de incluirla?

A ver si entendí. Esto hace que la librería se “auto-instancie” con solo el hecho de incluirla?

Exacto. De hecho si echas un vistazo a SD.h verás que al final está la instrucción extern SDClass SD; y al final de SD.cpp SDClass SD;.

Bueno, si eso aun así implica que va a consumir algo de memoria RAM

Hay que tener fe en el compilador y el depurador. Intentarán que si no usamos algo, no se cargue en el archivo final, pero sí. Estamos hablando de un objeto que hemos instanciado, y eso puede consumir memoria de por sí.

entonces mejor que el código “herede” esas funciones con solo incluir la librería. Será eso posible

Tú decides lo que es mejor para tu caso, aunque tal y como tienes el código, que consiste sólo en funciones, y no tiene propiedades, sí parece lo más apropiado. Si lo quieres hacer así, en tu byteestractor.h limítate sólo a declarar funciones:

#ifndef ByteExtractor_h
#define ByteExtractor_h

    // From variable's value
    void getBytesFromInt(unsigned int var, unsigned char *buffer);
    void getBytesFromInt(unsigned int var, unsigned char *buffer, unsigned int bOffset);
    void getBytesFromLong(unsigned long var, unsigned char *buffer);
    void getBytesFromLong(unsigned long var, unsigned char *buffer, unsigned int bOffset);

    // From an array of bytes
    unsigned int getIntFromBytes(unsigned char *buffer);
    unsigned int getIntFromBytes(unsigned char *buffer, unsigned int bOffset);
    unsigned long getLongFromBytes(unsigned char *buffer);
    unsigned long getLongFromBytes(unsigned char *buffer, unsigned int bOffset);
    void getBytesFromVar(unsigned char *var, unsigned char *buffer, unsigned int offset, bool isLongType);
#endif

En tu byteextractor.cpp las defines:

#include "ByteExtractor.h"

// Public functions

/* From variable's value */

void getBytesFromInt(unsigned int var, unsigned char *buffer)
{
  if (sizeof(buffer) < 2) {return;}
  unsigned char *pointer = (unsigned char*)&var;
  getBytesFromVar(pointer, buffer, 0, false);
}

void getBytesFromInt(unsigned int var, unsigned char *buffer, unsigned int bOffset)
{
  if ((sizeof(buffer) - bOffset) < 2) {return;}
  unsigned char *pointer = (unsigned char*)&var;
  getBytesFromVar(pointer, buffer, bOffset, false);
}

void getBytesFromLong(unsigned long var, unsigned char *buffer)
{
  if (sizeof(buffer) < 4) {return;}
  unsigned char *pointer = (unsigned char*)&var;
  getBytesFromVar(pointer, buffer, 0, true);
}

void getBytesFromLong(unsigned long var, unsigned char *buffer, unsigned int bOffset)
{
  if ((sizeof(buffer) - bOffset) < 4) {return;}
  unsigned char *pointer = (unsigned char*)&var;
  getBytesFromVar(pointer, buffer, bOffset, true);
}

/* From an array of bytes */

unsigned int getIntFromBytes(unsigned char *buffer)
{
  return getIntFromBytes(buffer, 0);
}

unsigned int getIntFromBytes(unsigned char *buffer, unsigned int bOffset)
{
  if ((sizeof(buffer) - bOffset) < 2) {return 0;}
  unsigned int var = 0;
  unsigned char *pointer = (unsigned char*)&var;
  for (unsigned char i = 0; i < 2; i++) {
    pointer[i] = buffer[i + bOffset];
  }
  return var;
}

unsigned long getLongFromBytes(unsigned char *buffer)
{
  return getLongFromBytes(buffer, 0);
}

unsigned long getLongFromBytes(unsigned char *buffer, unsigned int bOffset)
{
  if ((sizeof(buffer) - bOffset) < 4) {return 0;}
  unsigned long var = 0;
  unsigned char *pointer = (unsigned char*)&var;
  for (unsigned char i = 0; i < 4; i++) {
    pointer[i] = buffer[i + bOffset];
  }
  return var;
}

// Private functions
void getBytesFromVar(unsigned char *var, unsigned char *buffer, unsigned int offset, bool isLongType)
{
  unsigned char size;
  if (isLongType) {size = 4;} else {size = 2;}
  for (unsigned char i = 0; i < size; i++) {
    buffer[i + offset] = var[i];
  }
}

Y en el sketck las usas sin objeto asociado:

#include <ByteExtractor.h>
byte array[2];

void setup() {
  Serial.begin(9600);
  while (!Serial) {}

  for (int n = 20; n >= -20; n++) {
    getBytesFromInt(n, array);
    PrintArray(n);
    Serial.println();
  }
}

void loop() {
  // Nothing happens in the loop
}

void PrintArray(int number) {
  Serial.print(F("Array's content: "));
  for (byte i = 0; i < sizeof(array); i++) {
    Serial.print(array[i], HEX);
    Serial.print('\t');
  }
  Serial.println();
  Serial.print(F("Decimal number: "));
  Serial.println(number, DEC);
}

noter:
Hay que tener fe en el compilador y el depurador. Intentarán que si no usamos algo, no se cargue en el archivo final

Amén hermano, amén. De hecho, hasta traté de no hacer muchas líneas de código; con tal de que ocupe el menor espacio posible del programa (y eso que olvidé crear las funciones para los tipo float).

¡Estoy completamente agradecido contigo noter! :smiley:

Ahora gracias a ti, los ejemplos son compilables; y ya por fin ponerle una documentación apropiada a la librería.

Bueno, creo que marcaré como "SOLUCIONADO" este problema; y puede que muy pronto comparta la librería final con uds.

¡GRACIAS A TODOS!