Подтвердить что ты не робот

Можно ли динамически определить структуру в C

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

При использовании сетевых коммуникаций существуют другие проблемы, которые необходимо адресовать - в частности, "сущность". То есть данные на проводе, вероятно, будут включать в себя многобайтовые (2, 4, 8) целые числа, и сначала будут отправлены MSB или LSB, но если одна машина является малоконтинентальной (IA-32, IA- 64, x86/64), а другой - big-endian (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);, чтобы выгрузить определенную структуру в указанный файл, которому предшествует тег, чтобы помочь в отладке. Etc.

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

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