2014-09-23 31 views
5

voglio scrivere, in C (nessun sapore particolar, diciamo c11) un generico, un array const globale di struct, come il seguente pseudocodice:Array of struct e sizeof

void * generic[] ={&(struct a va),&(struct b vb)}; 

Ora voglio creare una funzione che, dato l'id della posizione della struct desiderata (sto pensando di usare un const per ogni id, hardcoded), la copierà.

Perché copy() è anche generica (accetta un void * come destinazione) avrebbe bisogno di conoscere la dimensione rigorosa, strega può essere specificato dal chiamante (un sacco di soggetto ad errori, ha già bisogno di fornire la struttura di destinazione e il diritto id) o conserverò anche un array parallelo di sizeof corretto per ogni struttura.

C'è un modo per automatizzare al momento della compilazione l'inizializzazione dell'array sizeof? O addirittura implementare una sorta di trucco per consentire all'utente di specificare solo passare il puntatore a struct senza il suo id, senza dover scrivere uno specialista get_struct_x()?

Nessuna allocazione dinamica (alloc(), variabile locale va bene) consentito, tutta la struttura e il contenuto di array generico sono noti al momento della compilazione.

Ci dispiace per il mio cattivo spiegazione, sentitevi liberi di migliorare/correggerlo.

Modifica di chiarimento: ho bisogno di copia completa di un know tipo struct da un array in cui sono memorizzati molti tipi struct, ma lo stesso tipo mai ripetersi. E ho bisogno di farlo da una generica funzione get che verrà chiamata da thread diversi, quindi prima e dopo la copia bloccherà un mutex, e voglio mantenere tutto il codice di blocco e di cast in un punto, per minimizzare il debug e creare test più efficace.

+0

Credo che la tua domanda è , "Posso scrivere una funzione che richiede un array di' struct's e un indice e un indirizzo di destinazione 'struct' come input - tale che la' struct' specifica sia vincolata 'da qualche altra parte'? " –

+1

"C'è un modo per automatizzare in fase di compilazione l'inizializzazione di sizeof array?" l'unico meccanismo a tua disposizione è il preprocessore. È l'utilità più comune utilizzata per l'astrazione di questo tipo di funzionalità. –

+0

E 'un'opzione per memorizzare la dimensione di ogni struct al suo interno? Ad esempio, aggiungi un membro 'int mySize' come primo membro in ogni struct e impostalo (in runtime) su' x.mySize = sizeof (struct x) '. – usr2564301

risposta

1

un mio amico [s] senza conto StackOverflow [/ s] (@Francesco) ha rielaborato la risposta @WhozCraig aggiungendo il (molto vecchio) X macro (grazie alla great article da Randy Meyers) per costruire il generico struct array E l'enum witch aiutano ad accedere alla struttura dell'array lasciando all'utente la possibilità di modificare un solo punto;

tornare a lavorare gli ho detto mi piacerebbe provare a fare la specifica getter struct (come ci saranno non ripetuto STRUCT TYPE nella matrice. Se si sbaglia, compilazione errore di tempo, così, alla alla fine della giornata piegheresti la testa mentre si sviluppa, ma una volta compilato sarà più robusto), quindi verificheremo la compilazione sul tipo della struttura richiesta; a casa l'aveva già implementato. KUDOS!

Poi di nuovo ho perso un po 'di tempo su di esso e faccio in modo che lo sviluppatore debba semplicemente digitare una volta la struttura, per evitare il disallineamento da quella parte; questo è stato l'inizio di una spirale di ottimizzazione;

  • a causa di specializzarsi e mettere, più bisogno di matrice di dimensione, in modo da rimuovere la struct generica
  • nessuna struttura inizializzato necessario, usiamo "anonimo" di inizializzazione struct
  • perché l'inizializzazione "anonimo", è è più difficile da accedere direttamente alla struttura
  • accesso diretto richiede la conoscenza del sistema, con il piombo alla consapevolezza
  • codice più leggibile
  • facile da esportare GENERIC_TABLE e definizione della struttura su file esterno indipendente
  • #ifdef su GENERIC_TABLE funziona bene, si sposta al colpo di testa esterna per incrementare la leggibilità
  • nessuna inizializzazione dinamica necessaria al di fuori variabile locale (senza spazzatura, incastonato amichevole)
  • estremamente facile da usare

costano

  • forse un po 'gonfio preprocessore?
  • sembra un xD molto OOP
  • si può chiamare ottenere e mettere con puntatore profani

TL; DR: qui il codice compilazione completa e semplice esempio:

#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; 
} 
0

mi renderebbe la dimensione della struttura del primo elemento in ogni struct. In modo da avere

struct a 
{ 
    size_t size; 
    int a1; 
    double a2; 
    // ... 
}; 

struct b 
{ 
    size_t size; 
    char b1[20]; 
    float b2; 
    // ... 
}; 

Ogni volta che si crea una struttura, assicurarsi di inizializzare il formato, ad esempio,

struct a *aptr = malloc(sizeof(struct a)); 
aptr->size = sizeof(struct a); 
aptr->a1 = 3; 
aptr->a2 = 100.5; 

struct b someB = { sizeof(struct b), "hello", 1.0 }; 

Poi, nella routine copia, è possibile lanciare il void * ad un size_t * per ottenere la dimensione della struttura, in quanto si sa che ogni struct avrà le dimensioni come il primo elemento.

+0

In genere mi piace questo approccio meglio delle altre due risposte, ma sfortunatamente l'OP ha esplicitamente dichiarato che 'malloc()' non è ammesso per il soluzione. Quindi no +1 :-( – cmaster

3

senza dover modificare le strutture di dati sottostanti, questo è l'unico metodo posso vedere dove questo sarà abbastanza gestibile. Ogni voce nella propria matrice è non un void*, ma piuttosto una struttura che contiene un void* (all'elemento) e un size_t (dati sizeof per quell'elemento):

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

typedef struct Entry 
{ 
    size_t len; 
    void const *elem; 
} Entry; 

#define ELEM_ENTRY(x) { sizeof(x), &(x) } 

struct A 
{ 
    int a1; 
    long a2; 
} a; 

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

struct C 
{ 
    unsigned int c1; 
    double c2[5]; 
} c; 

Entry generic[] = 
{ 
    ELEM_ENTRY(a), 
    ELEM_ENTRY(b), 
    ELEM_ENTRY(c) 
}; 
size_t generic_n = sizeof(generic)/sizeof(*generic); 


int main() 
{ 
    for (size_t i=0; i<generic_n; ++i) 
     printf("[%zu] = %zu\n", i, generic[i].len); 
} 

uscita

[0] = 8 
[1] = 12 
[2] = 44 

io penso questo è quello che stai cercando di fare. in caso contrario, lascerò cadere questa risposta. Notare che probabilmente questo non è come si desidera se l'elemento è un tipo di puntatore (ma è funziona come si desidera se è un tipo nativo ). Perciò stai attento.

+0

scusa per rimuovere la tua risposta accettata, ma vedi la mia risposta che fa qualche trucco con la macro X – Lesto