#define trükk

Fórumok

Sziasztok!

Letre kene hoznom par ilyesmi szimbolumot C preprocessz soran:

#define XYZ_WHAT  XYZ_42WHAT
#define XYZ_EVER  XYZ_42EVER
#define XYZ_LOLZ  XYZ_42LOLZ

Ahol a "42" az egy elore adott konstans, amit 1x szeretnek csak definialni az elejen. Ilyesmivel probalkoztam hogy:

#define __xyz_def(a,b)   XYZ_##a##b
#define XYZ_WHAT __xyz_def(42,WHAT)
#define XYZ_EVER __xyz_def(42,EVER)
/* ... */

Ez itten fentebb mar ugye egy fokkal - jobb de a preprocessz alatt nem tudom beleirni azt hogy:

#define NUM  42
#define __xyz_def(a,b)   XYZ_##a##b
#define XYZ_WHAT __xyz_def(NUM,WHAT)
/* ... */

Mi is volt erre a trukk? :) Vagy legalabbis ugy remlik hogy volt erre valami, de sehol sem lelem... 

Thx, A.

Hozzászólások

Szerkesztve: 2024. 03. 15., p – 11:44
#define __xyz_def(x) XYZ_ ## 42 ## x
#define XYZ_WHAT __xyz_def(WHAT)

Ez elég jó? Még nem jöttem rá, hogyan lehetne a 42-t kiszedni a saját sorába.

(Szerk: az első ## nem is kell.)

Ah, koszi, ez igy most a gyakorlatban tenyleg jo lesz - hogy a NUM/42 erteket csak 1x kelljen leirni ;) Most atirtam erre:

#define __XYZ_DEF(b)   XYZ_42##b

#define XYZ_WHAT __XYZ_DEF(WHAT)
#define XYZ_EVER __XYZ_DEF(EVER)
/* ...*/

Persze az eredeti kerdesfelvetes az tovabbra is erdekel(ne), de lehet hogy igy ez mar mint elso lepes egeszen jo.

#define NUM 42
#define CAT3(a, b, c) a ## b ## c
#define __xyz_def(num, what) CAT3(XYZ_, num, what)
#define XYZ_WHAT __xyz_def(NUM, WHAT)

Na, csak meglett. Halványan derengett, hogy valami dupla ugrás kell. Nehéz volt ráguglizni. Az __xyz_def-ben még nem tudsz összefűzni, innen ugrani kell egy másik makróra és ott már igen. Ne kérdezd, nem értem :)

Ízlés szerint az XYZ_-t hardcode-olhatod a CAT3-ba, neki nem kell a dupla ugrás, a NUM-nak kell.

Koszi! Nezem itten, es mar majdnem jo, de... szoval basszus, mar latom mi itten a gond nalam :(

Ha az egyik LULZ mar letezik mint korabbi definicio, akkor baj van:

#include <stdio.h>

#define NUM 42

int     arr[2];

#define XYZ_                    111

// #define      WHAT                    222

#define XYZ_11WHAT              arr[0]
#define XYZ_42WHAT              arr[1]


#define CAT3(a, b, c) a ## b ## c
#define __xyz_def(num, what) CAT3(XYZ_, num, what)
#define XYZ_WHAT __xyz_def(NUM, WHAT)

int main(int argc,char *argv[])
{
 arr[0]=1;
 arr[1]=2;

 XYZ_WHAT=137;

 printf("%d %d\n",arr[0],arr[1]);
 return(0);
}

Ez igy fasza: a NUM 11 ill 42 eseten tenyleg azt csinalja a kod amit varunk - de ha a `#define WHAT` elol kiszedem a kommentet akkor osszeomlik :( 

> de ha a `#define WHAT` elol kiszedem a kommentet akkor osszeomlik :( 

Sóhaj...

Kábé ez az a pont amikor el szoktam kezdeni kódgenerátort írni, egyszerűbb esetekben (mint itt) sima pythonban, bonyolultabb esetekben jinjával kombinálva.

De várjunk még, hátha van itt valaki okosabb nálam aki tudja a nyers c preproc megoldást.

Igen, de lehet rekurzívan #include-olni. :)
Nem tudom a megoldást az eredeti kérdésre, csak azt, hogy egyik ismerősöm rekurzív #include-okkal új programnyelvet fejlesztett, amit a preprocesszor interpretál.

Szerk.: a -fmax-include-depth opcióra oda kell figyelni :)

Egyszer a Boost preprocesszorával én is csináltam valami hasonlót, mint amiről az eredeti kérdés szólt, de mivel az egésznek volt egy erőteljes intellektuális önkielégítés jellege, ezért inkább felhagytam vele. (Nem emlékszem rá pontosan, hogy miről szólt a küldetés, de az biztos, hogy volt benne for ciklus. Mármint olyan, amit preprocesszor hajtott végre.)

... és ha nem akarod minden WHAT és EVER és egyéb sorba kiírni a NUM-ot, akkor még egy indirekció. A lényeg hogy a NUM fölött kell két szint, egy nem elég. Szép, mi? :)

#define NUM 42
#define CAT3(a, b, c) a ## b ## c
#define CAT3x(a, b, c) CAT3(a, b, c)
#define __xyz_def(what) CAT3x(XYZ_, NUM, what)
#define XYZ_WHAT __xyz_def(WHAT)

Meg merjem kérdezni, hogy mi az X-probléma ebben a történetben?

Most, hogy az Y probléma megoldódott, kérlek áruld el, mi volt az X probléma.

Az a baj hogy annyira nem oldodott meg teljesen (ld. kesobb), de azert ez igy mar hatarozottan jobb volt mint lett jobb lett mint volt :)

Igy cimszavakban a dolog hattere: MSP430FR5xx-es architekturara portolok epp FreeRTOSt. Mindezt olyforman hogy mint preemptive, mind kooperative, mind pedig event+interrupt driven task awakening uzemmodban is szepen menjen, erosen kovetve a "az operacios rendszerhez kotheto dolgokat csak interrupt handler kontextbol csinalunk" elvet. Ehhez szeretnek a Cortex-Mx-es rendszerekhez hasonlatos task yield muveletelt, azaz olyan yieldet amikor a (akar nemprivilegizalt) szoftver altal kivaltott, de relative alacsony prioritasu, hardveresen atfuttatott (azaz nem environment call jellegu) megszakitas inditja be a folyamatot. 

No, es mig a Cortex-Mx-re van ilyen hogy PendSV (amit ez a jo ARM is erre teremtett), RISC-V eseten is van (CSR: MIP, MSIP bit), legtobb mas lightweight hardveren nincs. Vagyis, hat, nem nagyon van. Vagyis, hat ugy kell keresni. Szoval ezeknek az MSP430-as mikrokontrollereknek viszont van elvezerelt GPIO bemenetu megszakitasuk (PxIES, PxIE, PxIFG), amihez tartozik X darab (x=1... X) vektor is. Es ezzel kapcsolatban ezt mondja a manual:

Software can also set each PxIFG flag, providing a way to generate a software-initiated interrupt.

Avagy, ha bealdozunk egy Px.y GPIO bemenetet, es egy teljes vektort, akkor azt felhasznalhatjuk az ARM-nal megszokott PendSV-re (vagy hasonlo ugye a RISC-V-n az MSIP-hez tarsithato 3-as mcause). Csak roppantul esznel kell lenni mert a/ a Px.y-t masra nem hasznalhatjuk (se nem input, se nem output) b/ a hozzatartozo PORTx_VECTOR-t sem igazan (vagyhat, maximum esszel) c/ nagyon elo kell kesziteni a GPIO regisztereket a fogadasukra (hardveresen float, legyen input, legyen beepitett pullup-pulldown-valamilyen ellenallas, szoval hogy meg veletlenul se... opcionalisan lehet output is, franc se tudja mi a jobb). Es ha itt barmit elrontunk ebbol, akkor joesetben gyorsan, rosszesetben a leheto legaljsabb helyen ut be a krach (vagy legalabbis lasd meg: unexpected exception failed successfully).

Az altalad kerdezett "X problema" pedig nem mas, mint ennek a megfelelo, portmacro.h-beli elokeszitese, hogy ott te mint felhasznalo csak az x-et meg az y-t kell hogy megadd:

/* A PendSV-alike syscall is implemented using Port 2.7 as interrupt flag: */

#define         PENDSV_VECTOR           PORT2_VECTOR            /* port number goes here */
#define         PENDSV_REG(p, reg)      P2 ## reg               /* port number goes here */
#define         PENDSV_PIN              7                       /* pin number goes here */

Ezalapjan pedig elkeszul a tobbi regiszter alias:

#define         PENDSV_REG_IFG          PENDSV_REG(PENDSV_PORT,IFG)
#define         PENDSV_REG_DIR          PENDSV_REG(PENDSV_PORT,DIR)
#define         PENDSV_REG_REN          PENDSV_REG(PENDSV_PORT,REN)
#define         PENDSV_REG_OUT          PENDSV_REG(PENDSV_PORT,OUT)
#define         PENDSV_REG_IE           PENDSV_REG(PENDSV_PORT,IE)

#define portYIELD()             do { PENDSV_REG_IFG |= (1<<PENDSV_PIN); } while(0)

Meg persze az RTOS porton beluli minden ami ezt felhasznalja, jol:

__attribute__ ((naked)) BaseType_t xPortStartScheduler( void )
{
 vPortSetupTimerInterrupt();
 PENDSV_REG_DIR &= ~(1<<PENDSV_PIN); /* Px.y: input */
 PENDSV_REG_REN |=  (1<<PENDSV_PIN); /* Px.y: pullup/pulldown enabled */
 PENDSV_REG_OUT &= ~(1<<PENDSV_PIN); /* Px.y: pulldown */
 PENDSV_REG_IFG &= ~(1<<PENDSV_PIN); /* Px.y: clear any pending interrupt, just in case */
 PENDSV_REG_IE  |=  (1<<PENDSV_PIN); /* Px.y: enable this PendSV-alike interrupt */
 portRESTORE_CONTEXT();
 asm volatile ( "reti" );
}

#ifdef __GNUC__ /* GCC */
__attribute__ (( interrupt(PENDSV_VECTOR), naked ))
#else /* CCS */
#pragma vector = PENDSV_VECTOR
__interrupt
#endif
/* here, PC and SR are already in the stack: */
void PendSV_Handler(void)
{
 portSAVE_CONTEXT();
 PENDSV_REG_IFG &= ~(1<<PENDSV_PIN);
 vTaskSwitchContext();
 portRESTORE_CONTEXT();
 asm volatile ( "reti" );
}

Mivel sok helyen el tud verezni a dolog ha rosszul allitod be a Px.y-t (es hardverkozeli dolog leven elegge varatlan is lehet), ezert kerestem/keresek erre jo megodlast. Mint a legfelso kodtoredek mutatja, ez meg mindig nem teljesen fasza mert az x-et 2x kell leirni... de igy meg mindig talan jobb mint az eredeti.... 

Jonak jo de a/ akkor tobb (akar teljesen kulonbozo) kontext handler kell (az eredeti portban kulon van preemptiv timer, a koopperativ yield es kooperativ timer handler is) b/ az interrupt request driven task awakening nem fog menni es/vagy nem lesz hatekony (es raadasul sok stack-et is eszik mert kvazi duplan ment mindent). Konkretan ennel a hardver-osszeallitasnal a rendszerhivas-alapu porthoz kepest ez a megoldas 2x gyorsabb baud rate mellett is lehetove tette az UART interruptok feldolgozasat ugy hogy 0-ra esett le a 10ms jiffy nagysagrendjebol a bejovo adatforgalom kesleltetese is.

Ahogy érteni vélem, a yield rendszerhívás hatására a kernel átadja a vezérlést valamelyik futásra kész processznek. Ebb arra gondolok, hogy van kernel, vannak rendszerhívások, és vannak processzek.

Ha ez stimmel, akkor (felteszem) a többi rendszerhívás is implememtálva van valahogy.

Valojaban ebben a nagyon-nagyon konnyu kis kornyezetben (MMU nelkul: multithreading + IPC + power management) a (sys)tick-en meg a yield-en kivul nincs szukseg mas rendszerhivasra. Persze lehet ugy implementalni a dolgokat hogy az IPC-s adok-kapok is rendszerhivasokon keresztul megy kozvetlenul, de az MMU nelkul nem ad plusz vedelmet (csak lassabb lesz). Ellenben a szinkron multiplexelt es/vagy blocking IPC hivasok (vagy a rokon muveletek, mint szemaforok/mutexek) mar "userspace" szinten hasznalnak rendszerhivasokat amikor varakozara keszteti az adott thread-et. A rendszerhivasokon beluli es/vagy azok kozotti konkret implementacio akkor fontos ha mindezeket meg hatekonyan is szeretned csinalni (lasd: latency, task awakening interrupt handlerbol, ilyesmik).

Meg persze ilyesmibe is belemehetunk hogy a hatekony critical section felepites / lebontas mennyire tekintheto rendszerhivasnak. Ebbe is vannak/lehetnek erdekes trukkok (pl globalis nesting counter amit a kernel hozzacsap a context-hez): egy ilyen RTOS portingnak pont az a lenyege hogy ezeket elrejtse elolunk. Ettol fuggetlenul a critical section felepites / lebontas szerintem egy olyan resz amit RTOS kornyezetben mar nem a vegfelhasznalo dolga. Ott vannak erre a szemaforok vagy (a nagyon-nagyon konnyu kis kornyezet miatt) kommunikalja le kozvetlenul az adott periferiaval ha kell (ez egy kicsit szubjektiv dolog mar, azt elismerem, szoval minden tovabbi nelkul szabad critical section-okat hasznalni, nem lehet belole baj :)).

Fogsz egy tetszoleges scriptnyelvet, amibol webes/parancssori kapcsolos/guis inputbol legeneralod a megfelelo header file-okat. 1. sorba kommenttel: // AUTOGENERATED, DO NOT MODIFY!!!, meg persze az utolso sorban megismetelve. Kommentbe beteheted mi es milyen kapcsolokkal generalta, igy konnyen ujra tudod generalni kicsit mas beallitasokkal

Persze ertem a makros/preprocessoros megoldas sporterteket is. Nem vallalhatja be Teve az osszes hulye projectet. :D

A strange game. The only winning move is not to play. How about a nice game of chess?