Classe "SafeString" una valida alternativa alla pessima "String" ...

Più volte, su questo forum, abbiamo sconsigliato la classe "String" per i vari problemi che essa provoca.

Il commento di "SparkFun" sulla classe "String" è, più o meno, il segunete: "la classe String (con 'S' maiuscola) in Arduino utilizza una grande quantità di memoria e tende a causare svariati problemi su progetti si una certa dimensione. tale clsasse dovrebbe essere fortemente evitata nella scrittura di librerie".

Similmente "Adafruit" all'incirca così commenta: "Nella maggior parte degli utilizzi, molti altri piccoli oggetti String vengono temporaneamente utilizzati forzando la nuova allocazione delle stringhe in una nuova area dell'heap e lasciando un grosso buco dove si trovava quello precedente (frammentazione della memoria)."

Noi stessi abbiamo più volte segnalato agli utenti che utilizzavano la classe "String": "NON sei su un PC dove c'è un sistema operativo ed un "garbage collector", sei su una piccola MCU con solo 2KBytes di SRAM, dove devi fare tutto tu e dove usare la classe "String", a causa dell'allocazione e riallocazione dinamica della memoria, porta quasi sempre ... [u]a grossi problemi[/u] e sicuri mal di testa !!!"

Sempre "Adafruit" da una chiara rappresentazione grafica del problema ...

... e suggersice, come alemno primo palliativo per evitare il crash, quello di usare il metodo String.reserve () che, se impedisce la frammentazione dell'heap, non fa nulla e non riduce l'utilizzo eccessivo della memoria. Inoltre, anche se si evita la frammentazione del "heap", la continua creazione di oggetti "String" e la copia di stringhe all'interno di essi (e tra di loro), è cosa estremamente lenta.

Continua ....

Il suggerimento, sempre da noi dato agli utenti che utlizzavano la classe “String” è stato: "Impara ad usare le stringhe classiche del C … ovvero semplici array di char terminati dal carattere null (0x00) e le funzioni che trovi nella libreria standard (… che, oltretutto, è automaticamente inclusa dal IDE) AVR libc ed, in particolare, quanto è in <string.h> ".

Sicuramente, se questa è la strada migliore, certamente NON è la più semplice, almeno per “i meno esperti”, che espone il programma al grosso rischio di “corruzione” della memoria se … l’utente sbaglia il dimensionamneto dei char array o se copia stringhe classiche in altre strighe classiche magari di dimensioni più piccole o se fa comunque operazioni sugli array usando un indice fuori del valore massimo, ecc. ecc. … tutte cose che, ribadisco “i meno esperti”, rischiano facilmente di fare.

In aiuto a tali “meno esperti” c’è oggi una nuova libreria, installabile dal “library manager” che è una valida alternativa alla classe “String” pur mantenendone tutte le semplificazioni ed è la “SafeString” ;D

La libreria “SafeString” è stata progettata dall’autore (tale Matthew Ford) per “i principianti” come sostituto sicuro, robusto e debuggable perl’utilizzo delle stringhe in Arduino!

La guida completa all’installazione (da library manager) ed all’uso di detta libreria, si trova QUI … ne consiglio una attenta lettura.

Se ne è parlato anche parecchio nel gruppo di Google dedicato agli “sviluppatori di Arduinio”, QUI, è la discussione ancora va avanti :slight_smile:

SafeString è sicuramente una valida alternativa alla classe “String”, offre le stesse facilitazioni, ma è estremamente sicura, evita i guai della “String” ed è ben manutenuta … consiglio di provarla ! :grin:

Fatemi sapere … :wink:

Guglielmo

P.S.: io comunque continuo ad usare le funzioni classiche che si trovano in <string.h> … ormai ho l’abitudine :smiley:

non so, non sono convinto... non metto in dubbio la "sicurezza" della classe, che guarderò con calma

la questione è che così facendo ci si allontana dallo spirito del C

ho visto (e temo che con questa classe ne vedremo tanti altri) gente che usa una String solo per non fare due print in successione, manco gli viene in mente di farlo

e mettergli una ulteriore "stampella" non sono sicuro che gli insegni a "camminare"

EDIT Ho dato una rapida occhiata a quella classe: ma si basa sulla pre-dichiarazione della dimensione massima dell'oggetto? e quindi dove sarebbe il suo vantaggio competitivo rispetto alla pre-esistente Pstring?

Standardoil:
e mettergli una ulteriore “stampella” non sono sicuro che gli insegni a “camminare”

Sicuramente, difatti per primo dico che la cosa migliore sarebbe imparare ad usare bene le funzioni di <string.h>, ma …l’esperienza ormai ci insegna che, per una buona fetta di utenza, sono cose già troppo complicate che la classe “String” gli semplifica … ecco, questa fornisce la stessa semplicità, con tutte le stesse facilitazioni (concatenazioni, confronti, ricerca di token, e tutto quello che è possibile fare con "String") nascondendogli l’uso delle funzioni di <string.h> che in realtà usa.

Guglielmo

Ah … dimenticavo, se hai obiezioni, critiche, suggerimenti, domande e quant’altro … ti prego di andare nell’apposito gruppo di “Arduino” e esponi il tutto li. :slight_smile:

Questo thread è SOLO per informare l’utenza sull’esistenza di un’alternativa e per cercare si aiutare (nei limiti del possibile) chi avesse difficoltà nel suo uso, NON per discussioni sulla libreria stessa … dato che l’autore NON fa neanche parte del forum e NON ci legge. ::slight_smile:

Grazie.

Guglielmo

Grazie Guglielmo, ottima cosa!

Anche se sono anche io della "vecchia scuola" (ossia ho imparato prima il C, e solo dopo sono arrivati C++ et similia) per cui credo che userò comunque sempre il modo classico, potrebbe essere sicuramente utile non solo per i meno esperti di programmazione in generale, ma anche per chi proviene da ambienti come Java o C# i quali hanno l'abitudine a quella classe di oggetti più "facili da maneggiare".

PS: non capisco perché, ma all'autore non interessa stare anche in questo che è il forum ufficiale di Arduino?? Boh..

Update: ho dato un’occhiata, ed in parte sono rimasto un poco deluso. La programmazione ad oggetti non avrebbe permesso di fare override della classe String, o anche crearne una apposita, analoga?

Magari alla prima occasione di provare ad usarla mi analizzerò meglio i sorgenti della SafeString e vista dall’interno forse mi dovrò ricredere, però, per dire, non capisco perché iniziare con dichiarazioni che attualmente sono del tipo:

createSafeString(stringOne, 35);

invece di quello che mi aspettavo di vedere, ossia o questo:

String stringOne(35);

oppure:

SafeString stringOne(35);

Insomma, si, tutto bello, bella la boiserie, ma non è che mi convinca appieno…

PS: si, so che c’è il Google Group dove ne parla anche lo stesso autore, ma intanto mi interessa l’opinione della “nostra” comunità che già conosco… :wink:

PS2: Tra l’altro la libreria contenuta in uno spazio GitHub che non è gestito dall’autore ma da uno (?) che semplicemente gli “presta” lo spazio è un’altra cosa che non concepisco…
Ossia mi chiedo: “perché”? Lasciamo stare il fatto (anomalo) che uno che sviluppa una libreria per Arduino ma non frequenta il forum ufficiale di Arduino, mi chiedo perché non abbia voglia di usare gli strumenti comuni che usano milioni di sviluppatori, e quindi gestire da sé la sua libreria (incluse le pagine su GitHub come versioni, request, wiki, eccetera). E’ timido? E’ pigro? E’ straniero?
Boh…

Eh NO, è giusto che sia fatta come è fatta, proprio per NON usare in alcun modo la classe String e distinguersi da essa.

Considera che, se hai letto il gruppo, aveva chiesto di rimpiazzare la String con la SafeString, ma ... pare che la String NON si tocca e quindi ... nomi diversi dato che ciascuna deve restare indipendete dall'altra.

Per il resto, ripeto, "De gustibus non est disputandum" ... :)

Guglielmo

gpb01: Considera che, se hai letto il gruppo, aveva chiesto di rimpiazzare la String con la SafeString, ma ... pare che la String NON si tocca e quindi ... nomi diversi dato che ciascuna deve restare indipendete dall'altra.

Guglielmo

Beh Vorrei vedere

La classe String fa parte della libreria standard del linguaggio, non è che la si cambia facile...

gpb01: Eh NO, è giusto che sia fatta come è fatta, proprio per NON usare in alcun modo la classe String e distinguersi da essa.

Ma non si "usa" la classe String se fa override, e neanche si modifica la String ma semplicemente la "sua" classe facendo override sulla String, "nasconde" le corrispondenti funzioni, e solo per i progetti dove questa viene usata. (voglio sperare che il core di Arduino internamente non usi le String... ;) )

Considera che, se hai letto il gruppo, aveva chiesto di rimpiazzare la String con la SafeString, ma ... pare che la String NON si tocca e quindi ... nomi diversi dato che ciascuna deve restare indipendete dall'altra.

Si, ma in ogni caso per me la soluzione migliore e più "pulita" era appunto di definire una classe "SafeString" ma rendendola visibile direttamente, e non tramite i "giochini" come mi pare faccia in quella libreria, e che costringono a fare dichiarazioni di variabili in quel modo "rozzo" :D

Standardoil: Vorrei vedere La classe String fa parte della libreria standard del linguaggio, non è che la si cambia facile...

Ma chi ha parlato di cambiare la classe String della libreria? Fare una classe che la eredita e ne fa override delle parti che vanno cambiate per gestire diversamente l'allocazione non è "cambiare" nulla.

Standardoil: La classe String fa parte della libreria standard del linguaggio, non è che la si cambia facile...

... in realtà, se andate a leggere, vedrete che era stato chiesto proprio di "riscrivere" la classe String seguendo quanto fatto nella SafeString ;D ... la richesta è stata declinata ...

Guglielmo

La dichiarazione, da quel che scrivi, semplicemente mi pare segua l'implementazione del creational design pattern. Disaccoppia la chiamata di creazione dall'oggetto creato. Il metodo da te preferito è il costruttore dell'oggetto. Entrambe le vie sono usate correntemente a seconda dei casi e a volte dei gusti, non ci vedo nulla di così strano o esotico.

Riguardo implementare una cosa nuova come override di una vecchia, la trovo, sinceramente, una porchieria. Tanto vale fare la classe da zero, come scrivi sotto, e implementare una interfaccia simile senza però nessun vincolo, cioè senza l'uso di una vera interfaccia, ma giusto per mantenere la familiarità in chi la utilizza.

Per il resto, io avrei debellato sia String che evitato questa implementazione, creando un po' di cultura sull'uso delle stringe C standard. A volte, si crede di semplificare la vita creando questi oggetti ma secondo me si semplificherebbe di più se non ci fossero, così la gente sarebbe costretta a imparare nel modo giusto fin da subito senza vedere delle false panacee. Occhio non vede cuore non duole. Parlo solo di Arduino ed MCU dove c'è il rischio concreto di frammentazione della memoria a causa di queste classi. E ve lo dico io che al C mi ci sono avvicinato solo con Arduino e quindi abituato da sempre ad usare la classe String in altri linguaggi. Qui, non ne sento minimamente la mancanza. Anzi, a volte mi riscrivo anche funzioni io perchè quelle già pronte richiedono troppe risorse, es. sprintf, quindi usare gli array di char, a maggior ragione, non mi cambia la vita.

Maurizio

docdoc: ... non capisco perché iniziare con dichiarazioni che attualmente sono del tipo: createSafeString(stringOne, 35); oppure: SafeString stringOne(35); Insomma, si, tutto bello, bella la boiserie, ma non è che mi convinca appieno...

Non capisco @docdoc, quella libreria usa un truccaccio (come hai scritto poi) con una macro per dichiarare un buffer fuori dalla dichiarazione dell'oggetto. Nella pagina github specifica che questo createSafeString(str, 40); viene "espanso" in char str_SAFEBUFFER[40+1]; SafeString str(sizeof(str_SAFEBUFFER),str_SAFEBUFFER,"","str");

Per me è un pò un accrocchio. Viola le regole di buona programmazione a oggetti. Se non ricordo male esiste anche una libreria (forse tu @docdoc l'avevi segnalata) PString3 dove a cura del programmatore dichiarare a priori un buffer, cosa che questa lib fa con trucchetto della macro.

Però, in tutti casi abbiamo un array di dimensione fissa, non di certo la facilità d'uso (ma con i problemi di micro-frammentazione memoria) di una allocazione dinamica. Non capisco quanto possa aiutare un utente alla prime armi. Rimango scettico.

Ok, mi rimangio quanto scritto sopra sul costruttore, un trucco e bistrucco non è certo un design pattern, e mi confermi ulteriormente la mia idea iniziale, che tanto vale usare gli array di char e debellare il resto.

Maurizio

nid69ita:
… Non capisco quanto possa aiutare un utente alla prime armi. Rimango scettico.

… aiuta nel senso che l’utente “alle prime armi” è abituato ad usare i metodi “semplici” della classe String … questa glie li fornisce senza usare le String e nascondendogli la complessità delle funzioni di <string.h> … PUNTO.

Nessun utente “alle prime armi” va a vedere come è fatta dentro, la usa e basta, quindi lo aiuta ad evitare i casini della classe String pur mantenendo la … semplicità d’uso.

Detto questo ribadisco quanto detto al post #4 … quindi, andate a discutere della bontà o meno di essa nel gruppo di “Google” degli sviluppatori Arduino … altri “commenti” sulla libreria verranno cancellati.

Guglielmo

maubarzi: tanto vale usare gli array di char e debellare il resto.

Concordo, e se è per questo io non avrei proprio implementato la String sotto Arduino. IMHO uno che vuole programmare microcontrollori non dico che debba saper programmare in Assembler, ma il C è fondamentale che lo conosca per restare più possibile "a basso livello" con l'hardware, per questioni sia di conoscenza di ciò che si sta gestendo, sia per maggiore controllo dell'efficienza del codice. Ma se anche fosse, mi vanno ad implementare una classe che fa uno spreco di risorse proprio su una MCU con pochissima RAM? Ok, implementala ma fallo "decentemente" (intendo entro i limiti dell'hardware ovviamente), ed un esempio è proprio questo SafeString che "maschera" la gestione classica C per gestire le stringhe "quasi" come su sistemi più evoluti, ma di fatto ad uso esclusivo degli utenti "abituati" a linguaggi e sistemi di livello più alto.

gpb01: ... in realtà, se andate a leggere, vedrete che era stato chiesto proprio di "riscrivere" la classe String seguendo quanto fatto nella SafeString ;D ... la richesta è stata declinata ...

Uh, non me lo spiego, ma ok, no comment...

Farò sicuramente una prova. Devo ammettere che per pigrizia non ho più modificato il mio programma come mi aveva intimato di fare Guglielmo. :D A mia discolpa c'è da dire che uso le stringhe in lettura quindi non c'è un consumo costante di memoria e non mi ha mai dato problemi.

Qualcuno ha provato a scrivere uno sketch consuma memoria con entrambe le librerie e vedere cosa succede? Una curiosità, le stesse raccomandazioni valgono anche per altre MCU, tipo una a caso, ESP32?

gianlucaf: Qualcuno ha provato a scrivere uno sketch consuma memoria con entrambe le librerie e vedere cosa succede? Una curiosità, le stesse raccomandazioni valgono anche per altre MCU, tipo una a caso, ESP32?

Cioè NON usare String, ? Assolutamente si. Su ESP32 hai solo più memoria SRAM, quindi la microframmentazione potrebbe insorgere dopo più tempo, ma i problemi intrinseci rimangono. Non hai un S.O. che fa pulizia/compattazione della memoria

nid69ita: Cioè NON usare String, ? Assolutamente si. Su ESP32 hai solo più memoria SRAM, quindi la microframmentazione potrebbe insorgere dopo più tempo, ma i problemi intrinseci rimangono. Non hai un S.O. che fa pulizia/compattazione della memoria

Non è del tutto corretta la tua affermazione. La gestione della memoria RAM su ESP32 è completamente diversa da quella degli Arduino AVR. Inoltre viene usato il kernel FreeRTOS che offre un minimo di gestione della memoria più avanzata (http://https://www.freertos.org/a00111.html). Ciò detto, tendenzialmente anche io evito di usare il più possibile la classe String, ma ci sono alcune situazioni in cui alla fine si rivela la scelta migliore e tutto dipende da come la usi. Ad esempio, se vuoi sviluppare una libreria per Arduino, dove la classe String è stra-usata soprattutto negli esempi, "constringere" l'utente neofita ad usare metodi più memory safe può essere complesso e controproducente (la maggior parte delle persone al primo segno di difficoltà passa oltre). Ecco allora che una bella funzione in overload può salvare capra e cavoli.

cotestatnt: ... Ciò detto, tendenzialmente anche io evito di usare il più possibile la classe String, ma ci sono alcune situazioni in cui alla fine si rivela la scelta migliore e tutto dipende da come la usi. Ad esempio, se vuoi sviluppare una libreria per Arduino, dove la classe String è stra-usata soprattutto negli esempi, "constringere" l'utente neofita ad usare metodi più memory safe può essere complesso e controproducente (la maggior parte delle persone al primo segno di difficoltà passa oltre).

Io non sono d'accordo. Una strada semplice ma sbagliata, porta solo a problemi, fa si che le persone si adagino e si abituino sempre alle cose semplici innescando un circolo vizioso verso il peggio. Le persone sono in grado di imparare ogni cosa, quando gli serve, se però gli affianchi una cosa facile, li rendi pigri e non si impegneranno mai a studiare perchè a prima vista non gli pare sensato e quando si accorgono che la strada facile è sbagliata, ormai è troppo tardi. Invece di ripartire nel modo corretto cercano accrocchi di ogni tipo pur di non cambiare, perchè a questo punto vuol dire dover rimettere mano a troppe cose. Lo vedo di continuo. E vedo che cose difficili, senza scorciatoie, vengono assimilate senza problemi se adeguatamente spiegate ma soprattutto senza che ci siano strade apparentemente più facili e consuete.

Maurizio