Nézzük az alábbit:
#define INTERRUPT_test_and_run(PIE, PIR, MASK, FNC) (((PIE) & (PIR) & (MASK)) ?\
(FNC, true) : false)
void __interrupt() INTERRUPT_InterruptManager(void) {
bool ready;
ready = false;
ready |= INTERRUPT_test_and_run(PIE3, PIR3, _PIR3_TMR0IF_MASK, tmr0_isr());
ready |= INTERRUPT_test_and_run(PIE4, PIR4, _PIR4_U1RXIF_MASK, rx_isr());
ready |= INTERRUPT_test_and_run(PIE4, PIR4, _PIR4_U1TXIF_MASK, tx_isr());
if (!ready) RESET(); // unhandled interrupt
}
Gondolhatja-e a fordító, hogy a RESET() hívásához szükséges ready változó helyes előállításához szükségtelen a makróban hivatkozott FNC meghívása? Mert ha ezt gondolja, akkor épp az interrupt handlert fogja kispórolni, és sovány vigasz lesz annak a néhány byte-nak a megspórolása. :) Veszélyes-e ebből a szempontból ez a makró?
Szerk.: Nem azt állítom, hogy most rosszul fordítja - még nem próbáltam ki -, hanem az a kérdés, hogy vajon ez egy annyira ügyetlen makró, amitől állandóan ott a fejem felett a pallos, hogy egyszer csak kispórolja a fordító az IT handler hívását, vagy ez így teljesen jó, netán teljesen rossz, vagy picit módosítani kellene rajta?
Megjegyzés:
Annak, aki esetleg érdekesnek gondolja, úgy tűnik, az eredeti makró - itt fentebb - hibás, s a fordító nem érzi szükségesnek a vessző operátor előtti függvény hívását, hiszen a kifejezés anélkül is kiértékelhető. Akkor viszont nem hívódik az interrupt handler.
Hozzászólások
volatile nem segít ilyesmin?
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?
Na, de mi legyen volatile? Az interrupt handler függvény?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
szerintem a ready / done változó
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?
Azt szerintem ki kell számítania, mert különben nem tudná a végén, hogy reset legyen, vagy sem. Itt az a bajom, hogy az (FNC, true) az az FNC hívása nélkül is true, tehát FNC hívása elhagyható a kifejezés eredményének szempontjából, viszont épp az FNC a katarzis ebben a dologban. :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Az ( FNC, true ) az vessző operátor akar lenni? Nincs valami olyasmi, hogy kiértékelési sorrend?
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?
Igen, vessző operátor. Ezt láttam oda a legalkalmasabbnak. Balról jobbra, és a leggyengébben „köt”.
Szerk.: Csak ugye FNC nélkül is tudható, hogy (FNC, true) az true, tehát nem kell FNC-t hívni. Szóval sajnos ez így nem jó, már látom.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Attól tekintsünk el, hogy a ready változó neve kifejezőbb volna, ha átnevezném done-ra. :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Részben megvan a válasz:
warning: (759) expression generates no code
Szomorú vagyok, át kell alakítani. Az előbb nem vettem észre, csak most. :(
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Ezt ki kellene vizsgálni, hogy pontosan mire mondja és miért.
Hacsak az nem, hogy a INTERRUPT_test_and_run első három paramétere fordítási időben ismert, ezért tudja a fordító, hogy a FNC mindig meghívódik, és a ready már háromszorosan nemnulla a függvény végére, tehát a RESET hívására sosincs szükség.
Ebben az a esetben viszont az a kérdés, hogy mi másnak kellene történnie?
Szerk: a vesszőoperátornak nem az a szemantikája, hogy 'hajts végre a vessző utáni részt', hanem az, hogy 'hajtsd végre a vessző előtti részt (mellékhatásokkal együtt), azután hajtsd végre a vessző utáni részt, a kimenet ennek a második résznek az eredménye'.
Szerk2: most talán jobban értem, hogy kifejezetten a vesszőoperátorra vonatkozott a kérdés. Szerencsére az előző kiegészítésben ezt is leírtam; most csak annyit tennék hozzá, hogy a függvényhívásnál a paramétereket elválasztó vessző nem vesszőoperátor, vagyis a paraméterek kiértékelése tetszőleges sorrendben történhet, akár futásonként változhat (tehát ne építsünk arra, hogy pl. az előző futásnál balról-jobbra volt).
Nem tudhatja. Ha engedélyezve van egy negyedik periféria, amelyik IT-t kér, akkor egyik maszkra sem lesz illeszkedés, és a végén a RESET()-nek kell futnia. Ha kiegyszerűsíthető lenne fordítási időben, nem írtam volna bele. Jó, tudom, rezeg a léc, mert alant NULL-ra vizsgálok, amikor sohasem hívom a függvényt NULL-al, de szerintem az azért úgy szép. :)
Igen, ez fontos kérdés. Tehát a vessző operátor esetén mindenképpen végre lesz hajtva a vessző mindkét oldalán található kifejezés? Akkor tehát mégis majdnem jó volt az eredeti makróm?
Igen, azt tudom, hogy függvényhívás esetén nem csinálhatom például ezt: fnc(x, x++); mert ennek csúnya vége lehet.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
> Ha engedélyezve van egy negyedik periféria, amelyik IT-t kér, akkor egyik maszkra sem lesz illeszkedés, és a végén a RESET()-nek kell futnia.
Off: Hát ezért lenne jó, ha az emberi olvasó meg tudná tippelné, hogy a PIE, PIR, MASK szimbolumok melyike #define-olt konstants, és melyike változó.
> Tehát a vessző operátor esetén mindenképpen végre lesz hajtva a vessző mindkét oldalán található kifejezés? Akkor tehát mégis majdnem jó volt az eredeti makróm?
Most zavarba jöttem, milyen lenne a "nem mindenképpen"? Mitől függene, hogy megcsinálja-e? (Jó, ha olyan speciális eset, hogy látja a fordító, hogy nincs mellékhatása, akkor lehet belőle egy 'useless calcluation' warning, de a függvényhívás az nem ilyen [bár ha a függvény is ugyanabban a source-ben van, és mondjuk üres a törzse, akkor akár ez is mehet].)
A PIE az a regiszter abszolút címmel, amelyiknek az egyes bitjei egyes perifériák IT kérését engedélyezik. Azért kell makró, mert van egy rakás ilyen regiszter, hiszen 8 bites az MCU, de több, mint 8 IT-t kérő periféria van. A PIR az a regiszter, amelyben 1-be billen a PIE-vel azonos helyiértéken lévő bit, ha teljesül az IT kérés feltétele, például soros porton beérkezett egy karakter. Ez akkor is bebillen 1-be, ha amúgy a PIE által tiltva van az IT kérés, csak ez utóbbi esetben nem keletkezik megszakítás. Azért kell mégis összemaszkolni PIE-t és PIR-t, mert olyan is lehet, hogy egy másik periféria kér IT-t, amiatt megyünk bele ebbe az IT rutinba - itt csak egyetlen IT vektor van -, és ha PIE tiltottsága ellenére csak PIR-t vizsgálnánk, tévesen kiszolgálnánk az amúgy tiltott IT-jű perifériát.
A MASK pedig kiszedi azt a bitet, amelyik perifériához tartozik a PIE és PIR a nyolcból.
Zavarosan fogalmaztam. Olyasmire gondoltam, mint például az || vagy && operátorok esetében, hogy amikor már lehet tudni az eredményt, végre sem hajtódik a további része ezzel időt spórolva, de ezt például pointereknél vaskosan ki is használjuk:
char c, *p;
while (p && *p && *p != c) p++;
Tehát akkor a vessző operátor esetében mindkét oldal mindenképpen kiértékelésre, végrehajtásra kerül, ha jól értem.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Így van, a rövidzár kiértékelés a ||, &&, ?: operátorok specialitása, a vesszőoperátor szemantikája az, hogy mindig végrehajta mindkét oldalt.
Szóval az eredeti kérdést tovább kell vizsgálni, mire vonatkozik a compiler-warning.
A sizeof operátor is érdekes:
Ez picit engem is zavarba tud hozni, hogy akkor most a tömb mérete, vagy pusztán a címének, egy pointernek a mérete, de az előbbi.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Várj, az más téma.
Az az egyik kérdés, hogy a sizeof kiértékeli-e az operandusát, vagy sem.
Az egy másik kérdés, hogy milyen környezetben konvertálódik a tömb típusú kifejezés a tömb első elemére mutató mutatóvá.
C99 6.3.2.1 Lvalues, arrays, and function designators, p3:
A VLA egy nagy... megkönnyítés, igen, azoknak a kedvéért, akiknek az alloca bonyolult.
Itt a
sizeof
futásidőben kérdez le valamit, természetesen nem aza
elemeit, hanem aza
előtt lévő nem publikus metainformációt.Az alloca() nem szabványos (sem POSIX, sem ISO C; most legalábbis C11-ig próbáltam megnézni).
Én mondjuk semmilyen formában nem támogatom külső adattól függő méretű tömb foglalását "auto" tárolási osztályban (= vermen). Egyik írásmóddal (alloca vagy VLA) sem definiált, mi történik akkor, ha nincs elég hely.
C11-től kezdve egyébként opcionális a VLA támogatás; C11 6.10.8.3 Conditional feature macros:
Érdemes idézni a szabványból:
Valamint
itt is (mivel ez példaként szerepel, ezért nem normatív, hanem informatív):
Ebben az a szép, hogy a szintaxis (a nyelv formális nyelvtana) diktálja, hogy a "," karakter mikor mi. A fenti paragrafus olyan függvényhívást mutat példának, hogy
azzal a kommentárral, hogy
De ami igazán szép szerintem, az a második megjegyzés: within the second expression of a conditional operator. (Kiemelés tőlem.) Saját példáim:
Az "a"-nak az elemei: { 1, 3 }.
A "b"-nek az elemei: { 3 }.
A "c"-nek az elemei: { 2, 4 }.
A "b"-nél a nyelvtan alapján a vessző nem tudja "félbevágni" a feltételes operátor második operandusát, az "a"-ban lévő vesszőnél viszont a feltételes operátort még el sem kezdtük, a "c-"ben lévő vesszőnél pedig már befejeztük.
A "b"-nek a szintaxisfája:
Ezt a fát hogyan készítetted?
Kézzel, NEdit-ben. Egyik ablakban a fát rajzoltam / írtam, a másikban nyitva volt a C99 szabvány. (Az egyes operátoroknál tárgyalja a helyettesítési szabályokat, de a végén az Annex A (informative) Language syntax summary tartalmaz összefoglalót is, ld. A.2.1 Expressions.) NEdit-ben nagyon jól lehet diagramokat rajzolni; fejlett támogatása van téglalap kijelölésére (lehet egérrel kijelölni, mozgatni, kivágni); van "fill selection with char" makrója (pl. ezzel lehet nagyon jól függőleges vonalakat húzni). Az Emacs nyilván többet tud, de én kb. 25 éve NEdit felhasználó vagyok.
Most csinaltam egy ilyesmit:
Ezalapjan olyba' tunik hogy kiertekeli, akkor is hogyha nem hasznalja fel az eredmenyt.
Köszönöm!
Akkor lehet, visszatérek a makrós megoldáshoz, bár a static inline függvény is majdnem makró, csak nem a preprocesszor helyettesít még szövegesen, hanem a fordító teszi az adott függvény törzsét a hívó helyre, ami kb. ugyanazt eredményezi szerencsés esetben.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Egyre kevésbé értem. Ha az a ready volatile, akkor nem ad a fordító warningot azzal, hogy a kifejezésből nem fordult kód, viszont byte-ra pontosan ugyanolyan hosszúságú kód fordul. Másfelől, ha nem is hívja FNC-t, a kifejezést szerintem nem spórolhatja le, ellenben honnan tudhatná, hogy a végén kell-e a RESET() hívása, vagy sem? Azt elhiszem, hogy FNC hívást kispórolja, de a kifejezést muszáj lenne kiértékelnie.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Mi ez a fordito egyebkent ha nem titok? `xc8` vagy barmi egyeb, gcc-alapu joszag?
xc8, v2.50, PIC18F26Q71
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Akkor viszont ott lehetnek turpissagok :/ Az az egyetlen fordito ami a bitfieldeket sem ugy ertelmezi ahogy azt illendo. Siman lehet hogy ezt sem ugy csinalja ahogy a `gcc` es/vagy ahogy kellene.
Szoval ha barmiben bizonytalan vagy, akkor disassembly...
Én inkább azt szoktam bizonytalanság esetén, hogy megkerülöm a problémát, és egy robusztus, mindenképp működő alakban írom le. Szerintem az inline függvényes megoldásomat nem tudja elszúrni, mert akkor már tényleg nem azt csinálja, ami oda van írva.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Hat, az `xc8` az a bitfield-eknel sem azt csinalta ami oda volt irva...
Nem úgy van, hogy 0-ás bittől lefoglal annyit, amennyit kérsz, a következő field-del folytatja, és így tovább? Gondolom, ha a szószélességnél - pl. 8 vagy 32 bit - több esetén folytatja a következő 8 vagy 32 bites szavon, illetve szintén gondolom, hogy egy field belsejébe nem esik szóhatár.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
De, ez van ott is, 16-bites hatarokon. De mas 8-bites architekturan meg nincs ez. Meg ugye miert pont 16 biten ne loghatna at, miert nem 32 bit az, vagy 64 bit, vagy... vagy barhol atloghat. Marmint oke hogy a C standard megengedo, de akkor ugye elegge oda a hordozhatosag.
Azért ha egy unsigned counter: 5; átlóg az architektúra bitszélességének határn, majd csinálsz egy var.counter++; utasítást, akkor abba megbolondul a fél világ, akkora kóddá fog fordulni. De optimális esetben is kivadássza a két címről a megfelelő biteket megfelelően összeillesztve, növeli, majd az eredmény megfelelő töredékeit az eredeti helyre pl. egy xor, and, xor-ral visszateszi. Ja, persze két helyre a két töredéket, előtte shiftelések is vannak.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
>,Marmint oke hogy a C standard megengedo, de akkor ugye elegge oda a hordozhatosag.
A micsoda van oda? A bitfieldek soha a fájó életben nem voltak hordozhatóak. Ja meg semmi más sem, ami nem a méretével osztható címen van (padding), vagy ami egynél több bájtból áll (endianness).
Imigy?
Igen, ez jónak látszik. Annyi csak, hogy a ready |= true; az egyszerűbb ready = true; alakban. :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Egyébként miért szokás a do ... while(0) makróban? Valami sima ({ }) nem lenne megoldás? Persze lehet, le sem fordul.
Másik, amit sohasem próbáltam ki, hogy vajon a p->member helyett működne-e a (*p).member alak.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Egyébként miért szokás a do ... while(0) makróban?
Mert akkor csinalhatsz ilyet:
Egyebkent nem.
Köszönöm. Nem gondoltam végig az összes lehetséges szintaxis esetét.
Szerinted a (*p).member működne a p->member helyett?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Szerinted a (*p).member működne a p->member helyett?
Az mas kerdes hogy forditva csinalom es a obj.memb-ektol szabadulok es objptr->memb-eket hasznalok ahol csak lehet (azaz kb mindenhol :]).
Arra utaltam, hogy noha kényelmes, jól olvasható a struktúrára mutató pointer tagja -> jelölés, lényegében redundáns. Bár, amikor a struktúra tag is pointer, ami mutat egy tagra, akkor nem volna túl jó ez:
(*(*p).ptr).member
Lényegesen jobban olvasható a p->ptr->member
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Elengedtem a makrót, végül ezt csináltam:
Kérdés, hogy a függvénypointer átadásánál rájön-e arra, hogy ez fordítási időben eldől, mert már akkor tudható a cím, és közvetlenül egy hívássá fordítható mindenféle futásidejű címszámítás nélkül.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Itt mire gondolsz?
mi az isr szerepe a feltetelben? hogy ne legyen NULL? de ha NULL akkor minek is hivod meg? :)
Egyebkent sztem ki fogja optimalizalni a fordito, de gyorsan ke tudod ellenorizni :]
Igen, néha szoktam beleírni efféle védelmeket. NULL ellen van. :) Már csak azért is, mert sok függvényemet írom meg úgy, hogy ha kérek tőle valamit, akkor átadom a változó címét, ha nem, akkor NULL-t adok meg neki, persze ettől nem borul fel:
int8_t valami(uint16_t *var) {
if (var) *var = kifejezés;
return 0;
}
uint16_t result;
valami(NULL);
vagy
valami(&result);
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Újabb élmények. Megnéztem, mit fordít az xc8, de buta, pedig -O2. A static inline függvényt az inline ellenére nem mindannyiszor helyettesíti, hanem CALL-lal hívja, minekutána valóban a stack-en keresztül megvalósította a függvénypointeres hívást. Az egész felhajtás 212 byte-tal lett hosszabb - ha jól emlékszem -, mint a statikus megoldásnál, amikor is makróval a preprocessor helyettesít a fordítás előtt.
Szóval visszatértem a makrós megoldáshoz. Nem a 212 byte fáj igazán, hanem az interrupt rutin hatalmas overhead-je.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Es -O3?
Úgy tudom, a -O3 és -Os nem free. Az xc32-ben már a -O2 sem free, bár ez most irreleváns.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE