Go Down

Topic: [codice completo] database di strutture (aka: giochiamo con il precompilatore) (Read 2008 times) previous topic - next topic

lestofante

in piccolo progetto avevo bisogno di accedere a più di una struttura da più thread: ecco cosa mi sono inventato, grazie all'aiuto di stackoverflow e di un caro amico (full story in english: http://stackoverflow.com/questions/26005389/array-of-struct-and-sizeof/26024713#26024713)

ma passiamo subito al codice, come al solito autoesplicativo  :smiley-mr-green: :smiley-mr-green: (seriamente, fate domande se avete bisogno)

Code: [Select]

#include <memory.h>
#include <stdio.h>
#include <stdlib.h>

/* BEGIN: just some struct to test, fell free to move them on external header file */
struct A
{
    int a1;
    long a2;
};

struct B
{
    float b1;
    char b2[6];
};

struct C
{
    unsigned int c1;
    double c2[5];
};
/* END: just some struct to test, fell free to move them on external header file */

/* this macro will create the right X macro element, and also initiliaze the "anonymous" struct */
#define ADD_STRUCT_TO_ARRAY(xu) X(xu, &(struct xu){})SEP

/* BEGIN: add or remove your own struct here! 
 * fell free to move them on external header file 
 * just need to pass struct name without "struct" to ADD_STRUCT_TO_ARRAY macro */
#define GENERIC_TABLE \
    ADD_STRUCT_TO_ARRAY(A) \
    ADD_STRUCT_TO_ARRAY(B) \
    ADD_STRUCT_TO_ARRAY(C)
/* END: add or remove your own struct here! */

/* here we initialize the enum, where the type of the struct is the key, and its position in the array the value */
#define SEP ,
#define X(a,b) a
enum STRUCT {
    GENERIC_TABLE
};
#undef X

/* here we initalize the array of pointer to the structure */
#define X(a,b) b
void * const generic[] =
{
    GENERIC_TABLE
};
#undef X
#undef SEP

/* here we create all the getter function. add here your array locking code */
#define SEP ;
#define X(a,b) void get_##a(struct a * dest){memcpy(dest, generic[a], sizeof(struct a) );}
GENERIC_TABLE
#undef X
#undef SEP

/* here we create all the putter function. add here your array locking code */
#define SEP ;
#define X(a,b) void put_##a(struct a * source){memcpy(generic[a], source, sizeof(struct a) );}
GENERIC_TABLE
#undef X
#undef SEP

/*run, code, run!*/
int main()
{
    struct A a_copy;
    struct B b_copy;
    struct C c_copy;

    get_A(&a_copy);
    get_B(&b_copy);
    get_C(&c_copy);

    printf("STRUCTURE IN ARRAY BEFORE INITIALIZATION\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);

    a_copy.a1 = -1;
    b_copy.b1 = 2.3;
    c_copy.c1 = 3;

    printf("STRUCTURE CHANGED TO\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);

    printf("STRUCTURE SAVED\n");
    put_A(&a_copy);
    put_B(&b_copy);
    put_C(&c_copy);

    get_A(&a_copy);
    get_B(&b_copy);
    get_C(&c_copy);

    printf("STRUCTURE LOADED\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);

    a_copy.a1 = 1000;
    b_copy.b1 = -50.576;
    c_copy.c1 = 700;

    printf("STRUCTURE CHANGED TO\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);

    get_A(&a_copy);
    get_B(&b_copy);
    get_C(&c_copy);

    printf("STRUCTURE RELOADED WITHOUT SAVING\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);


    return 0;
}

Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino


leo72

Generalmente quando io pubblico un codice, lo spiego per filo e per segno, non lascio interpretarlo agli altri  :P :smiley-yell:

Dunque, il problema qual è, di base?
Come lo risolve il tuo codice?
Che s/vantaggi rispetto alle soluzioni X, Y e Z?

Grazie  XD

(scusa ma sono a lavoro e non posso mettermi a studiare un programma non sapendo neanche bene cos'è che fa di base  ;) )

lestofante

allora, il problema è il seguente;
abbiamo una serie di thread (se voltere provare l'ebrezza anceh su uno/mega, provate ChibiOS) che devono accedere ad una serie di strutture (definite a compile time).

Ovviamente l'accesso dovrà essere sincronizzato ed atomico, ed il tutto deve essere "user friendly" per minimizzare gli errori.

l'idea di base è stata creare un array di puntatori void alle strutture, e poi una put e una get che, preso in input un puntatore alla stuttura desiderata, esegua il lock, la COPIA della stuttura (da array a struct utente o viceversa in base a get o put) e finalmente l'unlock. Questo previene letture e/o scritture in contemporanea.

Il codice per fare il lock() e unlock() non è inserito, in quanto varia in base alla piattaforma ed il sistema utilizzato, ma si concentra su come fare in modo che l'utente, semplicemente aggiungendo una sola riga, che è:
Code: [Select]
ADD_STRUCT_TO_ARRAY(A)
/*dove A è il nome della struttura privo di struct antecendente (in caso di errori, tranquilli, compilation error)*/


in automatico e in mdo trasparente abbia la struttura inizializzata, inserita nell'array, nella enum nome struttura -> indice nell'array (che teoricamente l'utente non deve usare) e la creazione di funzioni get_nomestruct(tipostrcuct *) e put equivalenmte.

Insomma, ogni elemento dell'array, pur NON essendo omogeneo ed essendo acceduto tramite void *, riesce cmq ad avere un controllo di tipo al momento della compilazione; questo è un problema che nemmeno i linguaggi ad oggetti risolvono, vedi il "ClasCastException" che ti si solleva a RUNTIME (in C semplicemente useresti un array di memoria con la struttura di un'altra area di memoria.. very !!FUN!!)


tutta la stora del "parto" di questo codice è nel link a stackoverflow
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

leo72

Tu parli di copia, ma allora se devo usare una delle strutture in realtà io vado a crearmene una copia prima di gestire il dato? Ma così non spreco risorse?

Altra domanda, gli RTOS più avanzati hanno già funzioni di semaforo e altro per evitare accessi concorrenziali, perché non usare un qualcosa predefinito dell'RTOS? Che vantaggi hai col tuo sistema?

lestofante

Quote
Tu parli di copia, ma allora se devo usare una delle strutture in realtà io vado a crearmene una copia prima di gestire il dato? Ma così non spreco risorse?

ma in cambio tieni bloccata la risorsa agli altri thread solo per il tempo di copia (che essendi in ram e spesso di pochi byte è velocissima). Non lo considero uno spreco

Quote
Altra domanda, gli RTOS più avanzati hanno già funzioni di semaforo e altro per evitare accessi concorrenziali, perché non usare un qualcosa predefinito dell'RTOS? Che vantaggi hai col tuo sistema?

ma il mio sistema USA le chiamate di sistema, solo che appunto fa in modo che le usi SOLO il mio sistema, visto che passandoti una COPIA non hai più bisogno di preoccuparti di accessi paralleli etc..

notare che la put SOVRASCRIVE sempre, il che vuol dire che se ci sono più producer, o un producer+consumer, una logica un poco più comeplessa va implementata, ma cmq è fattibile, solo che non mi era necessario averla.
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

leo72


lestofante

ti consiglio di spippolarti le istruzioni al preprocessore per generare l'array e le funzioni...

giusto per avere un'idea della potenza del C
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

leo72

Hai mai sentito parlare dei proto-threads?
Sono un'"invenzione" di un prof matematico.

E' un sistema basato sul semplice linguaggio C e le direttive al preprocessore per creare una sorta di gestione a thread del codice.
https://code.google.com/p/arduino-class/wiki/ProtoThreads

Ho provato la libreria ed effettivamente è una cosa carina.

lestofante

studierò  :smiley-mr-green:


edid: studiato.. sono stackless, una limitazione mica da poco. Per il resto sembrano davvero comodi
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

leo72

Sono comodi per task che non richiedono l'uso di un vero RTOS perché capisci che altro non sono che un modo per usare lo switch..case in un modo non.... ortodosso  ;) sfruttando cioè i limiti dello switch stesso che diventa la "feature" da usare  :P

Comunque era un esempio per far comprendere che ci sono tante cose da poter fare con il precompilatore  ;)

Go Up