få en substruct ut av en stor struct i C

stemmer
3

Jeg har en veldig stor structi et eksisterende program. Dette struct omfatter et stort antall bitfields.

Jeg ønsker å spare en del av det (si, 10 felt av 150).

Et eksempel kode jeg ville bruke til å lagre underklasse er:

typedef struct {int a;int b;char c} bigstruct;
typedef struct {int a;char c;} smallstruct;
void substruct(smallstruct *s,bigstruct *b) {
    s->a = b->a;
    s->c = b->c;
}
int save_struct(bigstruct *bs) {
    smallstruct s;
    substruct(&s,bs);
    save_struct(s);
}

Jeg ønsker også at du velger hvilken del av det ikke ville være for mye mas, siden jeg ønsker å endre det nå og da. Den naive tilnærmingen jeg presenterte før er svært skjøre og unmaintainable. Når skalere opp til 20 forskjellige felt, må du endre felt både i smallstruct, og i substructfunksjon.

Jeg tenkte på to bedre tilnærminger. Dessverre krever både meg å bruke noen eksterne CIL som verktøy for å analysere mine structs.

Den første tilnærming er å automatisk generere den substructfunksjonen. Jeg vil bare sette struct av smallstruct, og har et program som skulle analysere det og generere substructfunksjon i henhold til feltene i smallstruct.

Den andre tilnærmingen er å bygge (med C parser) en meta-informasjon om bigstruct, og deretter skrive et bibliotek som ville tillate meg å få tilgang til et bestemt felt i struct. Det ville være som ad-hoc implementering av Java klasse refleksjon.

For eksempel, forutsatt ingen struct-justering, for struct

struct st {
    int a;
    char c1:5;
    char c2:3;
    long d;
}

Jeg vil generere følgende meta-informasjon:

int field2distance[] = {0,sizeof(int),sizeof(int),sizeof(int)+sizeof(char)}
int field2size[] = {sizeof(int),1,1,sizeof(long)}
int field2bitmask[] =  {0,0x1F,0xE0,0};
char *fieldNames[] = {a,c1,c2,d};

Jeg får ith feltet med denne funksjonen:

long getFieldData(void *strct,int i) {
    int distance = field2distance[i];
    int size = field2size[i];
    int bitmask = field2bitmask[i];
    void *ptr = ((char *)strct + distance);
    long result;
    switch (size) {
        case 1: //char
             result = *(char*)ptr;
             break;
        case 2: //short
             result = *(short*)ptr;
        ...
    }
    if (bitmask == 0) return result;
    return (result & bitmask) >> num_of_trailing_zeros(bitmask);
 }

Begge metodene krever ekstra arbeid, men når parseren er i Makefile - endre substruct er en lek.

Men jeg vil heller gjøre det uten noen eksterne avhengigheter.

Har noen noen bedre idé? Hvor mine ideer noe bra, er det noen disponibel implementering av mine ideer på internett?

Publisert på 19/05/2009 klokken 13:59
kilden bruker
På andre språk...                            


5 svar

stemmer
11

Fra beskrivelsen din, ser det ut som du har tilgang til og kan endre den opprinnelige strukturen. Jeg foreslår at du refactor din understellet til et komplett type (som du gjorde i ditt eksempel), og deretter gjøre at strukturen et felt på den store strukturen, som innkapsler alle disse feltene i den opprinnelige strukturen i mindre struktur.

Utvide på lite eksempel:

typedef struct 
{
  int a;
  char c;
} smallstruct;

typedef struct 
{
  int b;
  smallstruct mysub;
} bigstruct;

Tilgang til smallstruct info vil bli gjort slik:

/* stack-based allocation */
bigstruct mybig;
mybig.mysub.a = 1;
mybig.mysub.c = '1';
mybig.b = 2;

/* heap-based allocation */
bigstruct * mybig = (bigstruct *)malloc(sizeof(bigstruct));
mybig->mysub.a = 1;
mybig->mysub.c = '1';
mybig->b = 2;

Men du kan også gå rundt pekere til den lille struct:

void dosomething(smallstruct * small)
{ 
  small->a = 3;
  small->c = '3';
}

/* stack based */    
dosomething(&(mybig.mysub));

/* heap based */    
dosomething(&((*mybig).mysub));

Fordeler:

  • ingen makroer
  • Ingen eksterne avhengig
  • Ingen minne ordens avstøpning hacks
  • Renere, enklere å lese og bruke kode.
Svarte 19/05/2009 kl. 14:14
kilden bruker

stemmer
3

Ved endring av rekkefølgen av feltene er ikke utelukket, kan man ordne bigstruct feltene på en slik måte at feltene er smallstruct sammen, og da det er ganske enkelt et spørsmål om å sende fra en til en annen (eventuelt tilsetning av en offset) . Noe som:

typedef struct {int a;char c;int b;} bigstruct;
typedef struct {int a;char c;} smallstruct;

int save_struct(bigstruct *bs) {
    save_struct((smallstruct *)bs);
}
Svarte 19/05/2009 kl. 14:11
kilden bruker

stemmer
1

Makroer er din venn.

En løsning ville være å flytte den store struct ut i sin egen inkluderer fil og deretter ha en makro fest.

I stedet for å definere strukturen normalt, komme opp med et utvalg av makroer, slik som BEGIN_STRUCTURE, END_STRUCTURE, NORMAL_FIELD, SUBSET_FIELD

Du kan deretter inkludere filen et par ganger, omdefinerer disse strukturene for hver passering. Den første vil slå forklaringene til en normal struktur, med begge typer felt sendes ut som normalt. Den andre ville definere NORMAL_FIELD har ingenting og vil skape din undergruppe. Den tredje ville skape den riktige koden for å kopiere undergruppe felt over.

Du vil ende opp med en enkel definisjon av strukturen, som lar deg styre hvilke felt som er i undergruppen og oppretter automatisk passende koden for deg.

Svarte 19/05/2009 kl. 14:13
kilden bruker

stemmer
0

Jeg foreslår å ta denne tilnærmingen:

  1. Forbanne fyren som skrev den store strukturen. Få en voodoo dukke og ha det moro.
  2. Mark hvert felt av stor struktur som du trenger en eller annen måte (makro eller kommentar eller hva)
  3. Skriv et lite verktøy som leser tekstfilen og trekker de markerte feltene. Hvis du bruker kommentarer, kan du gi hvert felt en prioritet eller noe å sortere dem.
  4. Skriv en ny header fil for understellet (med en fast topp- og bunntekst).
  5. Skriv en ny C-fil som inneholder en funksjon createSubStructsom tar en peker til den store struct og returnerer en peker til substruct
  6. I funksjon, loop over feltene samlet opp og slippe ut ss.field = bs.field(dvs. kopier feltene en etter en).
  7. Tilsett lite verktøy til din Makefile og legge til den nye header og C kildefilen til din build

Jeg foreslår å bruke gawk, eller noen skriptspråk du er komfortabel med, som verktøyet; som bør ta en halvtime å bygge.

[EDIT] Hvis du virkelig ønsker å prøve refleksjon (som jeg foreslår mot, det vil være en hel masse arbeid får som arbeider i C), deretter offsetof()makro er din venn. Denne makroen returnerer forskyvningen av et felt i en struktur (som oftest ikke er summen av størrelsene på feltene før den). Se denne artikkelen .

[EDIT2] Ikke skriv din egen parser. For å få din egen parser høyre vil ta måneder; Jeg vet siden jeg har skrevet mange parsere i mitt liv. I stedet markere deler av den opprinnelige header-fil som må kopieres og deretter stole på en parser som du vet virker: Den ene av C-kompilator. Her er et par ideer hvordan å gjøre dette arbeidet:

struct big_struct {
    /**BEGIN_COPY*/
    int i;
    int j : 3;
    int k : 2;
    char * str;
    /**END_COPY*/
    ...
    struct x y; /**COPY_STRUCT*/
}

Bare har verktøyet kopiere noe mellom /**BEGIN_COPY*/og /**END_COPY*/.

Bruk spesielle kommentarer liker /**COPY_STRUCT*/å instruere verktøy for å generere en memcpy()i stedet for et oppdrag, etc.

Dette kan skrives og feilsøkes i et par timer. Det ville ta så lang tid å sette opp en parser for C uten funksjonalitet; det er du vil bare ha noe som kan lese gyldig C men du har fortsatt å skrive den delen av parser som forstår C, og den delen som gjør noe nyttig med dataene.

Svarte 19/05/2009 kl. 14:45
kilden bruker

stemmer
0

Bare for å hjelpe deg i å få metadataene, kan du referere til offsetof () makro, som også har fordelen av å ta vare på noen padding du måtte ha

Svarte 19/05/2009 kl. 14:17
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more