recebendo um substruct fora de um grande struct em C

votos
3

Eu estou tendo um grande structem um programa existente. Esta estrutura inclui um grande número de bitfields.

Eu desejo para salvar uma parte dela (digamos, 10 campos em cada 150).

Um código de exemplo que eu usaria para salvar a subclasse é:

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);
}

Eu também desejo que escolher qual parte de que não seria muito trabalho, uma vez que gostaria de mudá-lo de vez em quando. A abordagem ingênua I apresentado antes é muito frágil e insustentável. Quando dimensionar até 20 campos diferentes, você tem que mudar campos tanto na smallstruct, e na substructfunção.

Pensei em dois melhores abordagens. Infelizmente tanto me obriga a usar alguns externo CIL como ferramenta para analisar meus estruturas.

A primeira abordagem é automaticamente gerar a substructfunção. Eu só vou definir a estrutura de smallstruct, e tem um programa que iria analisá-lo e gerar a substructfunção de acordo com os campos smallstruct.

A segunda abordagem é a construção (com analisador C) um meta-informações sobre bigstruct, e em seguida, escrever uma biblioteca que me permitisse aceder a um campo específico na estrutura. Seria como aplicação ad-hoc de reflexão classe de Java.

Por exemplo, assumindo que não há estrutura-alinhamento, para struct

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

Vou gerar as seguintes informações meta:

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

Vou pegar o ith campo com esta função:

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);
 }

Ambos os métodos exige um trabalho extra, mas uma vez que o analisador está na sua makefile - mudar o substruct é uma brisa.

No entanto, eu prefiro fazer isso sem dependências externas.

Alguém tem alguma idéia melhor? Onde minhas idéias qualquer bem, há alguma aplicação availible das minhas ideias na internet?

Publicado 19/05/2009 em 14:59
fonte usuário
Em outras línguas...                            


5 respostas

votos
11

De sua descrição, parece que você tem acesso e pode modificar a sua estrutura original. Eu sugiro que você refatorar seu subestrutura em um tipo completo (como você fez no seu exemplo), e, em seguida, fazer essa estrutura de um campo em sua estrutura grande, encapsular todos esses campos na estrutura original na estrutura menor.

Expandindo seu pequeno exemplo:

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

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

Acessando a informação smallstruct seria feito assim:

/* 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;

Mas você também pode passar em torno de ponteiros para a pequena struct:

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

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

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

benefícios:

  • não macros
  • Sem dependências externas
  • Não há memória de ordem hacks de fundição
  • Cleaner, mais fácil de ler e código de utilização.
Respondeu 19/05/2009 em 15:14
fonte usuário

votos
3

Se alterar a ordem dos campos não está fora de questão, você pode reorganizar os campos bigstruct de tal forma que os campos smallstruct estão juntos, e, em seguida, seu simplesmente uma questão de vazamento de um para outro (possivelmente adicionando um deslocamento) . Algo como:

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);
}
Respondeu 19/05/2009 em 15:11
fonte usuário

votos
1

Macros são seu amigo.

Uma solução seria para mover o grande struct fora em seu próprio arquivo de inclusão e, em seguida, ter uma festa de macro.

Em vez de definir a estrutura, normalmente, chegar a uma selecção de macros, tais como BEGIN_STRUCTURE, END_STRUCTURE, NORMAL_FIELD, SUBSET_FIELD

Você pode, então, incluir o arquivo algumas vezes, redefinindo as estruturas para cada passagem. O primeiro um girará as define em uma estrutura normal, com ambos os tipos de saída de campo sendo como normal. A segunda seria definir NORMAL_FIELD não tem nada e criaria seu subconjunto. O terceiro criaria o código apropriado para copiar os campos subconjunto mais.

Você vai acabar com uma única definição da estrutura, que permite controlar os campos que estão no subconjunto e cria automaticamente o código adequado para você.

Respondeu 19/05/2009 em 15:13
fonte usuário

votos
0

Eu sugiro a tomar esta abordagem:

  1. Amaldiçoar o cara que escreveu o grande estrutura. Obter um boneco de vodu e ter algum divertimento.
  2. Mark cada campo da grande estrutura que você precisa de alguma forma (macro ou comentário ou o que for)
  3. Escrever uma pequena ferramenta que lê o arquivo de cabeçalho e extrai os campos marcados. Se você usar os comentários, você pode dar a cada campo uma prioridade ou algo para classificá-los.
  4. Escrever um novo arquivo de cabeçalho para a subestrutura (usando um cabeçalho fixo e rodapé).
  5. Escrever um novo arquivo C que contém uma função createSubStructque leva um ponteiro para a grande estrutura e retorna um ponteiro para a substruct
  6. Na função, loop sobre os campos recolhido e emitem ss.field = bs.field(isto é copiar os campos de uma a uma).
  7. Adicionar a pequena ferramenta para a sua makefile e adicionar o novo cabeçalho e arquivo de origem C para a sua construção

Eu sugiro usar gawk, ou qualquer linguagem de script que você está confortável, como a ferramenta; que deve levar meia hora de construir.

[EDIT] Se você realmente quer tentar reflexão (que eu sugiro contra, ele vai ser um lote inteiro de trabalho que conseguir que o trabalho em C), em seguida, a offsetof()macro é seu amigo. Esta macro retorna o deslocamento de um campo em uma estrutura (que é na maioria das vezes não a soma dos tamanhos dos campos antes dele). Veja este artigo .

[EDIT2] Não escreva seu próprio analisador. Para obter o seu próprio analisador direito vai levar meses; Eu sei que desde que eu tenha escrito muitos analisadores na minha vida. Em vez marcar as partes do arquivo cabeçalho original que precisam ser copiados e, em seguida, contar com um analisador que você sabe que funciona: a do seu compilador C. Aqui estão algumas idéias de como fazer este trabalho:

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

Basta ter a sua ferramenta de copiar qualquer coisa entre /**BEGIN_COPY*/e /**END_COPY*/.

Use comentários especiais, como /**COPY_STRUCT*/para instruir a sua ferramenta para gerar uma memcpy()vez de uma atribuição, etc.

Isso pode ser escrito e depurado em poucas horas. Levaria mais tempo para configurar um analisador para C sem qualquer funcionalidade; que é que você tinha acabado de ter algo que pode ler válida C, mas você ainda teria que escrever a parte do analisador que entende C, e a parte que faz algo útil com os dados.

Respondeu 19/05/2009 em 15:45
fonte usuário

votos
0

Apenas para ajudá-lo na obtenção de seu metadados, você pode consultar a macro offsetof (), que também tem a vantagem de cuidar de qualquer preenchimento que você pode ter

Respondeu 19/05/2009 em 15:17
fonte usuário

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