Можно ли динамически определять структуру в C - PullRequest
23 голосов
/ 31 мая 2011

Я почти уверен, что этот вопрос станет действительно очевидным, и поэтому я не нашел много информации по нему. Тем не менее, я думал, что стоит спросить:)

По сути, доступ к данным с использованием структуры очень быстрый. Если данные поступают из сети в форме, в которой они могут быть немедленно обработаны как структура, это очень приятно с точки зрения производительности.

Однако возможно ли определить структуру динамически. Может ли приложение клиента и сервера согласовать формат потока данных и затем использовать это определение в качестве структуры?

Если нет, есть ли лучший способ сделать это?

Спасибо всем!

Ответы [ 6 ]

19 голосов
/ 31 мая 2011

Невозможно динамически определить структуру, идентичную структуре времени компиляции.

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

Кроме всего прочего, вы не можете получить доступ к члену somestruct.not_seen_at_compile_time, используя нотацию . или ->, если она не была определена во время компиляции.

С сетевыми коммуникациями есть и другие проблемы, в частности, «порядковый номер». То есть данные на проводе, вероятно, будут включать многобайтовые (2, 4, 8) целые числа, и MSB или LSB будут отправляться первыми, но если один из них имеет младший порядок (IA-32, IA- 64, x86 / 64), а другой - с прямым порядком байтов (SPARC, PPC, почти все, что не от Intel), тогда данные нужно будет преобразовать. Форматы с плавающей точкой также могут быть проблематичными. Существует множество стандартов, посвященных определению того, как данные будут передаваться по сети, - это не тривиально. Некоторые из них являются конкретными: IP, TCP, UDP; другие носят общий характер, например, ASN.1.

Однако, часть «не может создавать динамические структуры данных» ограничивает вещи - вы должны заранее договориться о том, что такое структуры данных и как они будут интерпретироваться.


Как ты это делаешь?

gerty3000 спрашивает:

Возможно, но сложно создать динамические структуры, которые могут содержать информацию, эквивалентную структуре. - Как вы это делаете? Я хотел бы передать динамически определенные структуры в другой код C (предположим, тот же компилятор и другие параметры) без необходимости дублировать процедуры компоновки структуры памяти из компилятора. Я не буду часто обращаться к полям этих структур внутри моего процесса (просто инициализирую их один раз), поэтому удобный синтаксис не имеет значения.

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

dynstruct.c

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

Он также содержит main() программу, которая тестирует код. Он вызывает other_function(), что демонстрирует, что структура, которую я определил в структурах данных, точно соответствует структуре. Данные предполагают 64-битный компьютер, где double должен быть выровнен по 8-байтовой границе (так что в структуре есть 4-байтовое отверстие); вам придется настроить данные для машины, где double может находиться на 4-байтовой границе.

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

/* This is the type that will be simulated dynamically */
/*
struct simulated
{
    int     number;
    double  value;
    char    string[32];
};
*/

/* SOF structure.h */
typedef enum Type { INT, DOUBLE, STRING } Type;

typedef struct Descriptor
{
    size_t  offset;
    Type    type;
    size_t  type_size;
    size_t  array_dim;
    char    name[32];
} Descriptor;

typedef struct Structure
{
    size_t      size;
    char        name[32];
    Descriptor *details;
} Structure;

extern void   *allocate_structure(const Structure *structure);
extern void    deallocate_structure(void *structure);
extern void   *pointer_to_element(void *p, const Descriptor *d);
extern int     get_int_element(void *p, const Descriptor *d);
extern void    set_int_element(void *p, const Descriptor *d, int newval);
extern double  get_double_element(void *p, const Descriptor *d);
extern void    set_double_element(void *p, const Descriptor *d, double newval);
extern char   *get_string_element(void *p, const Descriptor *d);
extern void    set_string_element(void *p, const Descriptor *d, char *newval);
/* EOF structure.h */

static Descriptor details[] =
{
    {   0,  INT,    sizeof(int),     1, "number"    },
    {   8,  DOUBLE, sizeof(double),  1, "value"     },
    {  16,  STRING, sizeof(char),   32, "string"    },
};

static Structure simulated = { 48, "simulated", details };

void *allocate_structure(const Structure *structure)
{
    void *p = calloc(1, structure->size);
    return p;
}

void deallocate_structure(void *structure)
{
    free(structure);
}

void *pointer_to_element(void *p, const Descriptor *d)
{
    void *data = (char *)p + d->offset;
    return data;
}

int get_int_element(void *p, const Descriptor *d)
{
    assert(d->type == INT);
    int *v = pointer_to_element(p, d);
    return *v;
}

void set_int_element(void *p, const Descriptor *d, int newval)
{
    assert(d->type == INT);
    int *v = pointer_to_element(p, d);
    *v = newval;
}

double get_double_element(void *p, const Descriptor *d)
{
    assert(d->type == DOUBLE);
    double *v = pointer_to_element(p, d);
    return *v;
}

void set_double_element(void *p, const Descriptor *d, double newval)
{
    assert(d->type == DOUBLE);
    double *v = pointer_to_element(p, d);
    *v = newval;
}

char *get_string_element(void *p, const Descriptor *d)
{
    assert(d->type == STRING);
    char *v = pointer_to_element(p, d);
    return v;
}

void set_string_element(void *p, const Descriptor *d, char *newval)
{
    assert(d->type == STRING);
    assert(d->array_dim > 1);
    size_t len = strlen(newval);
    if (len > d->array_dim)
        len = d->array_dim - 1;
    char *v = pointer_to_element(p, d);
    memmove(v, newval, len);
    v[len] = '\0';
}

extern void other_function(void *p);

int main(void)
{
    void *sp = allocate_structure(&simulated);

    if (sp != 0)
    {
        set_int_element(sp, &simulated.details[0], 37);
        set_double_element(sp, &simulated.details[1], 3.14159);
        set_string_element(sp, &simulated.details[2], "Absolute nonsense");
        printf("Main (before):\n");
        printf("Integer: %d\n", get_int_element(sp, &simulated.details[0]));
        printf("Double:  %f\n", get_double_element(sp, &simulated.details[1]));
        printf("String:  %s\n", get_string_element(sp, &simulated.details[2]));
        other_function(sp);
        printf("Main (after):\n");
        printf("Integer: %d\n", get_int_element(sp, &simulated.details[0]));
        printf("Double:  %f\n", get_double_element(sp, &simulated.details[1]));
        printf("String:  %s\n", get_string_element(sp, &simulated.details[2]));

        deallocate_structure(sp);
    }
    return 0;
}

other.c

Этот код ничего не знает о материале описания структуры в dynstruct.c; он знает о struct simulated, который имитирует код симуляции. Он печатает данные, которые он передал и изменяет его.

#include <stdio.h>
#include <string.h>

extern void other_function(void *p);

struct simulated
{
    int     number;
    double  value;
    char    string[32];
};

void other_function(void *p)
{
    struct simulated *s = (struct simulated *)p;

    printf("Other function:\n");
    printf("Integer: %d\n", s->number);
    printf("Double:  %f\n", s->value);
    printf("String:  %s\n", s->string);

    s->number *= 2;
    s->value  /= 2;
    strcpy(s->string, "Codswallop");
}

Пример вывода

Main (before):
Integer: 37
Double:  3.141590
String:  Absolute nonsense
Other function:
Integer: 37
Double:  3.141590
String:  Absolute nonsense
Main (after):
Integer: 74
Double:  1.570795
String:  Codswallop

Очевидно, этот код не готов к работе. Это достаточная демонстрация того, что можно сделать. Одна из проблем, с которой вам придется столкнуться, - это правильная инициализация данных Structure и Descriptor. Вы не можете поместить слишком много утверждений в такой код. Например, у меня действительно должно быть assert(d->size == sizeof(double); в get_double_element(). Также было бы разумно включить assert(d->offset % sizeof(double) == 0);, чтобы обеспечить правильное выравнивание элемента double. Или у вас может быть функция validate_structure(const Structure *sp);, которая выполнила все эти проверки. Вам понадобится функция void dump_structure(FILE *fp, const char *tag, const Structure *sp); для выгрузки определенной структуры в данный файл, перед которым стоит тег, чтобы помочь в отладке. И т.д.

Этот код является чистым C; он не компилируется компилятором C ++ как C ++. Недостаточно приведений, чтобы удовлетворить компилятор C ++.

9 голосов
/ 31 мая 2011

Нет, это не в C, все типы данных должны быть известны во время компиляции. Вот что делает его «очень быстрым».

2 голосов
/ 04 ноября 2016

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

Хотя в теории это очень привлекательно (звучит как самоизменяющееся приложение - ваше приложение должно было бы только сгенерировать код C для вашей структуры и вставить его в шаблон, затем попросить libtcc скомпилировать его и затем вызвать некоторые определенные функции в вашем шаблоне, чтобы использовать эту структуру), это решение, вероятно, не будет работать на практике. Зачем ? Что касается 2016 года, libtcc (и весь проект tcc) не очень активно разрабатывается, и есть проблемы с такими архитектурами, как x86_64.

1 голос
/ 13 марта 2018

Основываясь на ответе gerty3000, я создал библиотеку.Я абстрагировал что-то от конечного пользователя.Это было тяжело, но, наконец, сработало.Если есть какие-либо улучшения, я открыт для предложений.Вот код1008 * main.c // тестирование ....

#include "network.h"
#include <conio.h>

STATUS class_ex(Class * set) {
    class_data_push(set);
    if (set->data) {
        ClassField * field = set->field;
        while (field) {
            if (!strcmp(field->name, "peso")) {
                set->Double = 65.5;
            }
            if (!strcmp(field->name, "idade")) {
                set->Int = 29;
            }
            if (!strcmp(field->name, "nome")) {
                set->String = "Lisias de Castro Martins";
            }
            if (!strcmp(field->name, "endereco")) {
                set->String = "Rua Mae D'Agua";
            }
            class_mode(set, field, CLASS_INSERT);
            class_mode(set, field, CLASS_SHOW);
            field = field->next;
        }
        return (On);
    }
    return (Off);
}

int main(int argc, char** argv) {
    STATUS client_start = On;
    if (client_start) {
        Class * client = class_push("Client");;
        class_insert_back(client, BP64_T, "peso", 1);
        class_insert_back(client, B8_T, "endereco", 32);
        class_insert_back(client, B32_T, "idade", 1);
        class_insert_back(client, B8_T, "nome", 64);
        printf("Classe[%s]\n\n", client->name);
        if (class_ex(client)) {
        }
        class_pop(client);
        getch();
    }
    return (EXIT_SUCCESS);
}

Мне все еще нужно реализовать short double и некоторые другие функции, но он работает.

1 голос
/ 31 мая 2011

Вы не можете определить структуру на уровне источника, но вы можете сделать то же самое, настроив структуру данных для хранения имени / тега и смещения для каждого поля данных, которые вы хотите передать, и затем сохранить /читать данные с правильными смещениями в соответствии с этим.Убедитесь, что вы выравниваете все типы по границе, кратной sizeof(type) для мобильности.Конечно, если вы не уверены, что клиент и сервер будут иметь одинаковые представления данных (порядковый номер и другие факторы) и действительно нуждаются в производительности прямого доступа, я бы вместо этого написал правильные процедуры сериализации и десериализации ...

1 голос
/ 31 мая 2011

Для динамической структуры ответ - нет.

Если вы знаете, какие данные поступают, в C ++ вы можете использовать перегруженный оператор << in для чтения данных из потока. </p>

В C вы можете преобразовать поток в строку, предполагая, что вы знаете длину поступающих данных, и, используя функцию, подобную sscanf, вы можете прочитать данные.

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