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.
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