получение подструктуры из большой структуры в C - PullRequest
3 голосов
/ 19 мая 2009

У меня очень большая struct в существующей программе. Эта структура включает в себя большое количество битовых полей.

Я хочу сохранить его часть (скажем, 10 полей из 150).

Пример кода, который я использовал бы для сохранения подкласса:

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

Мне также хотелось бы, чтобы выбор какой-либо его части не был бы слишком сложным, поскольку я хочу менять это время от времени. Наивный подход, который я представил ранее, очень хрупок и недостижим. При масштабировании до 20 различных полей необходимо изменить поля как в smallstruct, так и в функции substruct.

Я подумал о двух лучших подходах. К сожалению, оба требуют, чтобы я использовал какой-то внешний CIL подобный инструмент для разбора моих структур.

Первый подход - автоматическое создание функции substruct. Я просто установлю структуру smallstruct, и у меня будет программа, которая будет анализировать ее и генерировать функцию substruct в соответствии с полями в smallstruct.

Второй подход - создание (с помощью C parser) метаинформации о bigstruct, а затем написание библиотеки, которая позволила бы мне получить доступ к определенному полю в структуре. Это было бы как специальная реализация отражения классов Java.

Например, при условии отсутствия выравнивания структуры, для структуры

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

Я сгенерирую следующую метаинформацию:

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

Я получу поле i th с этой функцией:

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

Оба метода требуют дополнительной работы, но как только синтаксический анализатор находится в вашем make-файле - смена подструктуры становится легкой задачей.

Однако я бы предпочел сделать это без каких-либо внешних зависимостей.

У кого-нибудь есть идеи получше? Где мои идеи полезны, есть ли доступная реализация моих идей в Интернете?

Ответы [ 5 ]

12 голосов
/ 19 мая 2009

Из вашего описания похоже, что у вас есть доступ и вы можете изменить свою первоначальную структуру. Я предлагаю вам реорганизовать вашу подструктуру в полный тип (как вы это сделали в своем примере), а затем превратить эту структуру в поле большой структуры, инкапсулируя все эти поля в исходной структуре в меньшую структуру.

Расширение на ваш маленький пример:

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

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

Доступ к информации о мелкой структуре будет сделан так:

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

Но вы также можете передавать указатели на небольшую структуру:

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

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

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

Преимущества:

  • Нет макросов
  • Нет внешних зависимостей
  • Нет хаков на заказ в памяти
  • Более чистый, легкий для чтения и использования код.
3 голосов
/ 19 мая 2009

Если об изменении порядка полей не может быть и речи, вы можете переставить поля большой структуры таким образом, чтобы поля маленькой структуры были вместе, и тогда это просто вопрос преобразования из одного в другое (возможно, добавление смещение). Что-то вроде:

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);
}
1 голос
/ 19 мая 2009

Макросы - ваш друг.

Одним из решений было бы переместить большую структуру в собственный включаемый файл, а затем создать макропартию.

Вместо обычного определения структуры, выберите макросы, такие как BEGIN_STRUCTURE, END_STRUCTURE, NORMAL_FIELD, SUBSET_FIELD

Затем вы можете включить файл несколько раз, переопределяя эти структуры для каждого прохода. Первый из них преобразует определения в нормальную структуру, причем оба типа полей выводятся как обычно. Второй определит, что NORMAL_FIELD не имеет ничего и создаст ваше подмножество. Третий создаст соответствующий код для копирования полей подмножества.

В итоге вы получите одно определение структуры, которое позволит вам контролировать, какие поля находятся в подмножестве, и автоматически создаст подходящий для вас код.

0 голосов
/ 19 мая 2009

Предлагаю воспользоваться этим подходом:

  1. Прокляни парня, который написал большую структуру. Получить куклу вуду и повеселиться.
  2. Отметьте каждое поле нужной вам большой структуры (макрос, комментарий или что-то еще)
  3. Напишите небольшой инструмент, который читает заголовочный файл и извлекает отмеченные поля. Если вы используете комментарии, вы можете дать каждому полю приоритет или что-то для их сортировки.
  4. Создать новый файл заголовка для подструктуры (используя фиксированный верхний и нижний колонтитулы).
  5. Создать новый C-файл, содержащий функцию createSubStruct, которая берет указатель на большую структуру и возвращает указатель на подструктуру
  6. В функции переберите собранные поля и введите ss.field = bs.field (т.е. скопируйте поля одно за другим).
  7. Добавьте небольшой инструмент в ваш make-файл и добавьте новый заголовок и исходный файл C в вашу сборку

Я предлагаю использовать gawk или любой язык сценариев, который вам удобен, в качестве инструмента; на это нужно потратить полчаса.

[РЕДАКТИРОВАТЬ] Если вы действительно хотите попробовать рефлексию (против чего я предлагаю; это будет очень много работы для работы в C), то макрос offsetof() - ваш друг. Этот макрос возвращает смещение поля в структуре (чаще всего это , а не сумма размеров полей перед ним). См. эту статью .

[EDIT2] Не пишите свой собственный парсер. Чтобы получить собственный парсер, понадобятся месяцы; Я знаю, так как я написал много парсеров в моей жизни. Вместо этого отметьте части исходного файла заголовка, которые необходимо скопировать, а затем положитесь на один из известных вам синтаксических анализаторов: на ваш компилятор Си. Вот несколько идей, как заставить это работать:

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

Просто попросите ваш инструмент скопировать что-нибудь между /**BEGIN_COPY*/ и /**END_COPY*/.

Используйте специальные комментарии, такие как /**COPY_STRUCT*/, чтобы поручить вашему инструменту генерировать memcpy() вместо назначения и т. Д.

Это может быть написано и отлажено за несколько часов. Это займет столько же времени, чтобы настроить синтаксический анализатор для C без какой-либо функциональности; то есть у вас просто есть что-то, что может прочитать действительный C, но вам все равно придется написать часть синтаксического анализатора, которая понимает C, и часть, которая делает что-то полезное с данными.

0 голосов
/ 19 мая 2009

Просто, чтобы помочь вам в получении ваших метаданных, вы можете обратиться к макросу offsetof (), который также имеет преимущество в заботе о любом заполнении, которое у вас может быть

...