Creación de un Sketch por carpetas

Supongamos que deseas que tus Sketch's estén organizados por carpetas, porque vas a tener muchas librerías y tenerlas todo junto te vuelve loco:

Prog ----- UnaCosa
|-- OtraCosa
|-- OtraMas
+- LaUltimaCosa

En tu código debes incluir las librerías así:

#include "UnaCosa/superlibreria.h"
#include "OtraCosa/Libreriapreciosa.h"

Pero ocurre que si tienes .h y .cpp, no va a encotrar las .cpp, no te da error de compilación típico, te da este cuando instancias un objeto:

undefined reference to SuperObjetoQueEstabaEnSuperlibreria

La solución es muy sencilla:

#include "UnaCosa/superlibreria.h"
#include "UnaCosa/superlibreria.cpp"
#include "OtraCosa/Libreriapreciosa.h"
#include "OtraCosa/Libreriapreciosa.cpp"

Y ya puedes tener tus librerías organizadas por carpetas

Yo ahora mismo para mi proyecto que aun no ha arrancado, tengo como 25 librerías, no quiero ni imaginar donde acabaré, así que imagínate verlo todo dentro de una carpeta, cuasi imposible

Otra opción es meter dentro del .h el .cpp (Esperando que la gente proteste en 3, 2, 1.....) pero si usas librerías de terceros, te desaconsejo que las modifiques, por si hay actualización, simplemente declararlas de esa manera.

Una cosa más, debido a que nadie hace bien los .cpp (deberían tener también otro #ifndef para no volver a redefinir los métodos de los objetos) si hacéis lo que comenté anteriormente, si se incluye la librería más de una vez, tendréis problemas de redefinición, insisto, porque un .cpp bien hecho debería tener también traducción condicional.

Es preferible en ese contexto generar un objeto propio que maneje el objeto/librería que vais a utilizar, y dentro de un namespace, para que no de problema alguno

Solo un par de observaciones.

En vez de usar:

#include "UnaCosa/superlibreria.h"
#include "UnaCosa/superlibreria.cpp"
#include "OtraCosa/Libreriapreciosa.h"
#include "OtraCosa/Libreriapreciosa.cpp"

Basta solo con incluir los .CPP

#include "UnaCosa/superlibreria.cpp"
#include "OtraCosa/Libreriapreciosa.cpp"

Al incluir el .cpp, ya estarás incluyendo el .h (generalmente, la primera línea de código).

Por otro lado, el principio de las librerias de arduino no es ese. Por ejemplo, supongamos que “superliberia” define una lista de enteros ordenada. Es una clase genérica que vas a usar en ese proyecto y/o quizás en otro.

Para ello tenemos la carpeta “libraries” que no solo sirve para descargarnos librerias de terceros, si no también para crear las nuestras propias.

Con esto no te digo que lo que propones este mal. Solo que no es aconsejable, los nuevos en el mundo de arduino que se encuentren con esto se confundirán y ten en cuenta que muchos son programadores noveles/nulos.

debido a que nadie hace bien los .cpp

No entiendo bien esa afirmación, ¿donde lo has visto? y explica mejor.

Vamos por partes que es largo

1º gracias, una gran idea lo de hacer include del .cpp aunque creo que no me sirve, pero te cuento y gracias otra vez, hoy estás que te sales…

2º en el mundo habemos 2 tipos de programadores, los que dividen sus librerías en .h y .cpp y los que no lo hacemos y sólo tenemos .h

El problema es el paradigma de ubicación de una librería. Hay dos paradigmas de ubicación: librerías locales al proyecto y librerías generales o globales. Eso lo tenemos claro todas y todos.

Los que programan .h, sea mono o multi fichero lo hacen perfecto, lo hacen multi paradigma, por dos razones:

  • Colocan un ifndef al principio y evitan problemas a las personas que sin darse cuenta, sobre todo los corta y pega código, incluyen por error varias veces la librería en su programa (¿te acuerdas @gatul del muchacho del otro día?)
  • Como yo includeo mi librería con #include ó #include “tal”, decido qué paradigma utilizo.

Pero aquí viene el problema de las .cpp: Suelen ser monoparadigma.

Muchas personas hacen en su código .cpp #include si están pensadas para que sean genéricas, además no tienen el #ifndef, con lo cual, nos encontramos con el problema, cuando no están bien declaradas, que primero, aunque yo me la lleve a local, ella busca <>, empezamos mal.

Por otro lado, como no tienen #ifndef, si se vuelve a includear, genera un problema de compilación, porque te dice el compilador que estás volviendo a definir algo que ya está definido.

Por otra parte, muchas veces, cuando generas una librería que usa otra librería, ya la estás condenando a un paradigma.

Mira lo que pasa:

La librería de Ardafruit que gestiona el sensor TSL2591 comienza de esta manera:

#include <Adafruit_Sensor.h>
#include <Arduino.h>
#include <Wire.h>

Acaba de obligar a que se use un paradigma determinado, por mucho que yo tenga la librería Adafruit_Sensor.h en local, la busca en global.

Antes de nada, decirte que la culpa de todo la tiene @surbyte (así, haciendo amigos de primera).

El otro día dio un gran consejo, como siempre, y es precisamente controlar las versiones de las librerías, porque si se modifican las librerías estandard, puede ocurrir que pasado el tiempo, tu solución deje de funcionar, porque una actualización de dicha librería puede ser incompatible con el tiempo con tu uso de ella, y tiene más razón que un santo, como decimos en la península.

Una solución es tener tu versión portable de Arduino IDE (ya se va comprendiendo mi otro hilo), pero esa solución al final no me gusta.

Otra solución es utilizar las librerías en local, la cual me convence, pero en ese caso, necesito, algo que odio, ver si esas librerías inducen un paradigma de ubicación o no.

Adafruit programa muy bien sus librerías, teniendo presente que ofrecen soluciones globales y aveces se tornan muy complicadas, pero es lo que sucede cuando ofreces una solución global, que lo tienes que solucionar todo.

¿Qué sucede?

Que si incluyo el .cpp tengo que modificar el .cpp y además el .h, y eso me molesta.

¿Cual es la opción más sencilla para mi en este momento?

He modificado el .h así:

//#include <Adafruit_Sensor.h>
#include <Arduino.h>
#include <Wire.h>

Si, hice comentario la linea que forzaba un paradigma.

Ahora yo debo controlar qué includeo o qué no includeo y cómo. Claro si uso un namespaces, y más si hago un objeto que controla esa librería, lo controlo muy bien, si no tengo que modificar ambos archivos, porque el .cpp siempre no se programa todo lo abstracto que el .h, a eso me refiero con que no se suele programar bien los .cpp

Hay que recordar que también existe el concepto de biblioteca, que es el que defendemos los programadores de sólo .h: yo tengo una librería de librerías, donde cada librería es lo más pequeña posible, y de esa manera el .h no se me hace incontrolable, que es la honorabiísima razón por la cual la gente divide sus librerías en dos, para tener por un lado algo pequeño donde ver qué se hace, y si hace falta se abrirá la que me dice cómo.

Perfecto.

Pero si trabajas bibliotecas, puedes tenerlo igualmente controlado y muy pequeño:

* ----------------------------------------------------------------------------
    Biblioteca de manejadores de EV's teórico.

    Nivel de abstracción en toda la librería, no de resolución concreta de
    ningún paradigma ni problema.

    Información técnica en rec/Fotometría

    Autor:    Tony Diana
    Versión:  20.11
    Licencia: (CC BY-NC-SA 4.0)
   ----------------------------------------------------------------------------
*/

#ifndef EV_h
#define EV_h


// --- Librerías del sistema
#include <Arduino.h>


// --- Librería
namespace EV {   // --- Espacio de nombres propio

  /* --------------------------------------------------------------------------
        Constantes de EV
     --------------------------------------------------------------------------
  */
  const PROGMEM float K_TERCIO = 1.0/3.0;   // --- Valor de un tercio
  const PROGMEM int K_PASOS = 3;            // --- Mis pasos son en tercios
  const PROGMEM float K_EV0 = 0.0;          // --- Cuanto vale un EV a 0.

  String K_UN_3  = " & 1/3";        // --- Cómo se escribe +1/3
  String K_DOS_3 = " & 2/3";        // --- Cómo se escribe +2/3


  /* --------------------------------------------------------------------------
        Librerías
     --------------------------------------------------------------------------
  */

  // --- Abstracción máxima en el manejo de EV's
  #include "EV_ABS.h"

  // --- Diafragmas (f=Apertura, F=Distancia focal, odio F mayúscula en f/)
  #include "EV_f.h"

  // --- ISO como objeto constante
  #include "EV_ISO.h"

  // --- Tiempo de exposición (T/V) como objeto constante
  #include "EV_TV.h"

}   // --- Espacio de nombres propio

#endif

El tema es que ahora, he de decidir si modificar un solo archivo o los dos, en fin, todo un tema

1 Like

Para salir de dudas, en el caso de Adafruit, que lógicamente tiene cuidado con su código, su .cpp comienza de esta guisa:

#include "Adafruit_TSL2591.h"

Aunque no tiene namespace ni #ifndef, así que no queda más remedio que también modificar el archivo .h

// # incluye <Adafruit_Sensor.h>

Con comentario, de esa manera puedo utilizar el .cpp en el include, pero lo tengo que contener todo con un namespaces, por si acaso.

Entiendo el concepto: usar librerias de terceros y tuyas en la misma carpeta de tu sketch para mantener un control de versiones.

El "pero" viene dado por como está diseñado el ide que no soporta subcarpetas. Se pueden crear y usar tal y como has expuesto pero esto no te libra de tener que editar los ficheros de las librerias de terceros.

Por ejemplo, la libreria del tsl2951 hace uso de la libreria "adafruit/sensor". Si creas tu estructura de carpetas:

/proyecto
|
|-- proyecto.ino
|
|-- /Adafruit_Sensor
|   |
|   |-- Adafruit_Sensor.h
|   |-- Adafruit_Sensor.cpp
|
|-- /Adafruit_tsl2591
    |
    |-- Adafruit_tsl2591.h
    |-- Adafruit_tsl2591.cpp

Encontrarás muchos problemas. El primero que hemos visto es que ignora el .cpp; el segundo que tienes que incluir las rutas de cada fichero; el tercero, que cada libreria que dependa de otra deberá incluir la ruta relativa para encontrar la dependencia.

Cuando el IDE hace la compilación, crea una carpeta temporal en la que
copia los ficheros .h y .cpp, pero no copia las subcarpetas...

En conclusión: siempre vas a tener que editar ficheros, con lo que no importa si usas más técnicas, namespace por ejemplo, lo vas a tener que hacer si o si.

He buscado alguna solución, a ver si en la "issues" encontraba algo y lo más razonable que he visto es cambiar de IDE. Por ejemplo, PlatformIO parece ser que permite usar una estructura de subcarpetas sin tener que modificar nada.

Creo que será Surbyte quien te podria indicar ya que sabemos que el trabaja con PlatformIO.

No sé si el Ide Pro o el CLI las soportan.

Muchas gracias por la información, confieso que no me he metido con el marcianito (plarformio) precisamente por dos motivos:

  • Aun estoy aprendiendo Arduino y el C++ todavía me da algo de guerra, ya que mi experiencia es más con Python y C. Necesito poder preguntar a la comunidad.
  • Me ha dado pereza lo confieso :frowning:

El caso es que sí, me va a tocar modificar una biblioteca estándar, y eso es como una falta "ética" en el mundillo del software, además que complica el mantenimiento para personas que no sea yo mismo, lo cual tendré que resolver aumentando la documentación del proyecto.

De todas maneras, me parece interesante poder controlar versiones de librerías sin depender de que el IDE te actualice las librerías y pierdas el control.

No sé si alguien lo considerará interesante o no, pero si lo fuese puedo publicar aquí el objeto que voy a construir para controlar ese sensor de luz, que simplificará su manejo, creo.

De todas maneras estoy esperando que el proveedor nacional lo importe, ya que trae muy pocos, e importarlo a mi me resulta más caro.

Además quiero que mis estudiantes, nada duchos en informática ni electrónica, tengan la capacidad de aventurare a modificar alguna que otra parte del código, y claro, el IDE de Arduino es más sencillo que configurar todo un VSC que es con lo que trabajo.

Hay ya no me puedo meter. Si yo fuera profesor enseñaria por un lado a crear librerias usando la carpeta “libraries” y como instalar, modificar, etc. A no ser que fuera un curso de C++ avanzado, entonces sería otro tema. Por eso te decia que no era aconsejable, ya que los novatos se van a perder y mucho, ten en cuenta que si les quitas delay les da algo imagina si le metes conceptos muy avanzados de C++: namespaces, polimorfismos, herencia multiple…, cuando ni siquiera entienden el concepto de setup/loop.

Dado que estamos en la sección de DOCUMENTACION y no de DEBATE, yo haria un tutorial, explicando la diferencia entre incluir con “” o con <> primero, acto seguido como incluir solo una libreria en una subcarpeta y como solucionar los problemas con esta, luego añadir otra libreria dependiente y como resolver las dependencias. Todo teniendo como pre-requisito saber manejar librerias en “libraries”. Podrías poner como ejemplo esa libreria que estas haciendo.

Ok haré eso dame unos días y lo preparo ¿te parece?

Me parece bien.