C String láma

Fórumok

Ritkán kell C-ben dolgoznom, leginkább mikrokontrollerek esetén, és akkor is zömében Arduino környezetben, ahol elég kényelmes String osztály érhető el.

Most azonban ESPIDF alapon kellene SVG-t generálnom, sok mérési adat megjelenítéséhez. Ehhez sok stringet kellene összefűznöm, és sprintf műveletekkel kitöltenem. Kerestem valami kényelmesebben kezelhető String osztályt, amit használhatnék, de nem találtam. Nekiállhatnék megírni, de olyan nehezen hiszem el, hogy ezt ne csinálták volna már meg ezren.

Tudtok valami kényelmesen használható C String osztályt, amit egyszerűen használhatnék? Fontos az append és az sprintf szerű formátumkezelés.

Hozzászólások

Mintha lattam volna C++-t ESPIDF-hez. Nem foglalkoztam vele igazan, mert be vagyok oltva ellene :)

Hogy string osztaly keresel C-hez, azt nem ertem...

Ez lehetséges, de nálam pont fordított a helyzet. ESP IDF projektben kellene Arduino API-t használnom, és ez nem ment.

Arra, hogy egy magot kikapcsoljak a rendszer felügyelete alól, és csak mintavételezésre használjam, ESP IDF projektet találtam mintául.

Lehet, hogy működne a módszer Arduino projekt alapon is, de az átállást erre és tesztelést egyelőre nagyobb szopásnak érzem, mint ESP IDF alatt stringet kezelni.

Később lehet, hogy tesztelni fogom azért ezt a módszert is, mert Arduino környezetben sokkal több könyvtár elérhető.

Ehhez sok stringet kellene összefűznöm, és sprintf műveletekkel kitöltenem.

String összefűzésre esetleg strcat egy for ciklusba téve?
 

A sok allokálással és a memória töredezésével van gondom, arról nem is szólva, hogy ha én írok meg valamit, abban biztos lesz még három olyan bug, amit más már leszopott. Tehát nekem valami ilyesmi felület kellene:

#include "LamaString.h"
...
LamaString line = LamaString.sprintf("<line x1='%d' y1='%d' x1='%d' y1='%d'/>",x1,y1,x2,y2);
LamaString svg = "<svg>".append( line ) . append( "</svg>" );

Persze, ha C-ben senki nem használja így a Stringeket, akkor implementálom magam, csak azt gondoltam, ez azért elég alap probléma ahhoz, hogy már sok megoldás született rá.

strcat(string1, string2); // Mindkettőnek "\0"-ra kell végződnie.

> Sol omnibus lucet.

snprintf-re az általános megoldás, hogy először NULL-t adsz át a célpointer helyett, akkor az snprintf kiszámolja neked, hogy mennyi hely kell. Allokálod azt a helyet, és utána a második snprintf hívásnak már az újonnan allokált helyet adod át. Kérdés, hogy ez neked belefér-e az időkorlátba. 

Idővel lassú lesz, mivel strlen(string1)-gyel kezdődik. Jobb követni, hogy hol jár a string vége:


char *p= buff;
char *plim= buff + sizeof buff;
p += sprintf(p, "Hőfok=%d", 37);
p += sprintf(p, ",páratartalom=%s", foo);
p += strlcpy(p, ",kiegeszites", plim-p);

Meg ugye az sprintf helyett jobb lenne egy snprintf, esetleg így:


unsigned mysnprintf(char *to, size_t size, const char *fmt, ...) {
    va_list ap;
    int len;
    unsigned retlen= 0;
    if (size==0) goto RETURN;
    va_start(ap, fmt);
    len= vsnprintf(to, size, fmt, ap);
    va_end(ap);
    if (len<=0) {
        retlen= 0;
    } else if ((unsigned)len >= size) {
        retlen= size-1;
    } else {
        retlen= len;
    }
/*  to[retlen]= '\0'; ha nem bizunk 100%-ig a vsnprintf-ben */
RETURN:
    return retlen;
}

char *p= buff;
char *plim= buff + sizeof buff;
p += mysnprintf(p, plim-p, "Hőfok=%d", 37);

Szerintem maradj a kályhánál. A string.h lib nagyon sztenderd, minden platformon van, nem az Arduino sajátossága, felesleges feltalálni a kereket. Ez C, ami 50+ éve fel van találva, nem Rust meg stb., ahol modern mindenfélével lehet lustulni. Igen, nem szép a printf, sprintf szintaxisa, ma már archaikusnak és hieroglifának hat, de kis gyakorlással hozzászoksz. Most még kényelmetlen, mert nem szoktál hozzá.

Esetleg ha csak SVG készítése a cél, akkor ahhoz lehet jobban járnál valami interpretált scriptnyelvvel, Python, Lua, Perl, ahol elérhető külön SVG-s modul, ha nem zavar, hogy kicsit lassabban fog futni, mint egy lefordított bináris.

A computer is like air conditioning – it becomes useless when you open Windows.” (Linus Torvalds)

Szerkesztve: 2022. 03. 01., k – 16:42

Amikor routerek fw-ét programoztam, a legnagyobb félelmünk egy esetleges underrun/overflow probléma, az invalid pointer dereferencing, illetve a memory leak volt.
Kezdetben a BSD-s fiúk féle ún. n-es függvényekkel dolgoztam. Ebben az volt a szar, hogy könyvelnem kellett a hosszokat is (vagy minden sztringműveletnél leszámoltatni), illetve nem volt automatikus újraallokáció.
Aztán mivel később más miatt is kellett glib, áttértem annak a GString típusára és függvényeire. Ez megoldja az allokációt egy köztes réteggel (valójában slice-okat allokál, és szükség esetén ezekből könyvel Neked területet, tehát nem futsz bele durva allokációs overhead-be valami szerencsétlen esetben, mert a lib ezt okosan kezeli).

De ezek x86 procis eszközök voltak, már akkor GB-os nagyságrendű memóriával. Egy ESP32-n valószínű nem ez lesz az optimális megoldás.

A másik véglet, amikor számológépre fejlesztettem C-ben (TI-89-re), ott mindent magadnak meg kellett csinálni. Amit csak lehetett, igyekeztem pointer-aritmetikával megúszni.
Nem tudom mennyire összetettek a sztringműveleteid. Ha kb. azonos substring-eket kell csak variálnod, esetleg közéjük fosni néhány értéket, akkor ezekből minél többet érdemes statikusan tárolni a memóriában (akár az egyes számjegyeket is!), és a manipuláló függvények egy ,,pointer-string"-en vagy tömbön dolgoznának.
Saját printf() implementációd pl. egy ilyen pointer-string-et kap, aminek minden szava egy pointer: kiírja az adott címen lévő string-et, \0 elérésekor ugrik a következő pointerre, és így tovább.
Amikor értékeket kell behelyettesíteni, van, hogy a leggyakoribb értékeket vagy akár az egyes számjegyeket is letárolom egy sztringben, és a pointerükkel hivatkozok rájuk. Olcsóbbra jöhet ki, mint egy ,,okos" printf() függvényt hívni, formátumsztring-feldolgozással. (HP kalkulátoroknál egyébként RPL nyelven is szokás ez: a számok talán 20-ig elérhetőek memóriacímen is, ami jóval olcsóbb, mintha értékként kellene feldobni őket a stack-re.)

Embedded hardverre - hacsak nem találsz valami nagyon jó kész lib-et - érdemes a hw sajátosságait figyelembe véve megírni néhány nagyon egyszerű elemi függvényt, és ezekre építeni. Szerintem.

Szerkesztve: 2022. 03. 01., k – 22:30

Most azonban ESPIDF alapon kellene SVG-t generálnom, sok mérési adat megjelenítéséhez.

Mekkora lesz az az SVG, ami itt készül? Annak fényében, hogy a ketyerédben van kb. 300KB RAM, én úgy gondolom, hogy mondjuk olyan 20-30KB-ig vagy jó, ha ennél nagyobb lenne a fájl, akkor jön az a verzió, hogy raksz mellé egy full featured OS-t futtató gépet (mondjuk valami linuxos *Pi, sok-sok száz MB RAM-mal), oda átküldöd az adatot minimális formázással, és majd ott csinálsz belőle weboldalt.

És 20-30KB-ig pedig statikus, fix méretű pufferbe gyűjteném a fájl tartalmát.

+1 Mikrokontrolleren a sting libekkel az lesz a probléma, hogy folyton dinamikusan akarnak allokálni memóriát.

Ilyen környezetben ha csak lehet fix pufferbe kell dolgozni. Ha nem fér be a memóriába az eredmény, akkor pedig streamelni kell, ahogy előáll az eredmény egy része már küldeni is kell a célnak és újrahasznosítani a buffert.

Van egy jópofa template megoldás, ami fejlesztési időben csinál template<->forráskód konverziót: https://github.com/qgears/rtemplate#rtemplate

Ezzel csináltam már olyat, hogy mikrovezérlő állított elő hosszú generált szöveget, amit egy nyomtatóra streamelt. Ez a leghatékonyabb módja a szöveg generálásnak mind template szerkesztés, mind runtime teljesítmény szempontjából azok közül amit ismerek.

Igen, lehet, hogy nálam is ehhez kell majd folyamodni. Jelenleg 600 mintavételezést tudok begyűjteni (60us), ha az ESP generálja hozzá az SVG kiértékelést. De ha javascripttel tudnék SVG-t generálni - márpedig, ha jól látom, lehet -, akkor az ESP-nek csak be kellene gyűjteni az adatokat, és átküldeni a böngészőnek, amiben a javascript majd kiértékeli. Mivel egy mintavételezés eredménye 4 bájton tárolható, várhatóan 60000 mintát is be tudnék (6ms) gyűjteni az elemzéshez. Már, ha valami egyszerű formában, át tudok küldeni 60000 4 bájtos bináris adatot a javascript részére. Ennek még nem jártam utána.