C Переменная Список членов для структур, это возможно? - PullRequest
1 голос
/ 07 апреля 2011

У меня есть вопрос о структурах, имеющих «список переменных-членов», аналогичный «списку переменных-переменных», который мы можем определить как функции.Я могу звучать глупо или совершенно не в духе с точки зрения основ языка Си, но, пожалуйста, исправьте меня, если я ошибаюсь.

Так что я могу иметь структуру C, подобную этой:

<code> 
    struct Var_Members_Interface
    {
        int intMember;
        char *charMember;
        ... // is this possible?
    };

Моя идея состоит в том, чтобы иметь интерфейс в стиле ac, который может быть реализован классами, но эти классы могут иметь дополнительные члены в этой структуре.Однако они должны иметь intMember и charMember.

Заранее спасибо.

Ответы [ 6 ]

2 голосов
/ 07 апреля 2011

Ближайшим приближением в C99 (но не в C89) является наличие гибкого элемента массива в конце структуры:

struct Var_Members_Interface
{
    int   intMember;
    char *charMember;
    Type  flexArrayMember[];
};

Теперь вы можете динамически распределять структуру с помощью массива типа Type в конце и получите доступ к массиву:

struct Var_Members_Interface *vmi = malloc(sizeof(*vmi) + N * sizeof(Type));

vmi->flexArrayMember[i] = ...;

Обратите внимание, что это не может быть использовано в C ++.

Но это не очень близко к тому, что вы ищете.То, что вам нужно, не может быть выполнено в C с одним типом структуры и может быть аппроксимировано в C ++ только через наследование - см. Другие ответы.


Один трюк, с которым вы можете справиться - обычно - вC использует несколько типов структур и множество приведений:

struct VM_Base
{
    int   intMember;
    char *charMember;
};

struct VM_Variant1
{
    int   intMember;
    char *charMember;
    int   intArray[3];
};

struct VM_Variant2
{
    int   intMember;
    char *charMember;
    Type  typeMember;
};

struct VM_Variant3
{
    int     intMember;
    char   *charMember;
    double  doubleMember;
};

Теперь, с некоторыми бросками кувалдой, вы можете написать функции, которые принимают аргументы 'struct VM_Base *' и передают указатель на любой из VM_VariantN типы.'intMember', вероятно, может использоваться, чтобы сказать, какой из вариантов у вас есть на самом деле.Это более или менее то, что происходит с функциями сокетов POSIX.Существуют разные типы адресов сокетов, и структуры имеют разную длину, но они имеют общий префикс, и в результате вызывается правильный код, поскольку общий префикс идентифицирует тип адреса сокета.(Дизайн не элегантный, но он был стандартом - де-факто стандартом для сокетов BSD - до того, как POSIX его стандартизировал. И дизайн BSD предшествовал C89, не говоря уже о C99. Он разрабатывался сейчас с нуля, без каких-либо требованийдля совместимости с существующим кодом это было бы сделано по-другому.)

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

1 голос
/ 07 апреля 2011

Вы можете сделать это так, как это делала старая библиотека виджетов X11 Xt :

struct Var_Members_Interface {
    int intMember;
    char *charMember;
};
struct Other_Part {
    int extraInt;
    char *extraString;
}
struct Var_Other_Interface {
    struct Var_Members_Interface base;
    struct Other_Part other;
};

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

struct Var_Other_Interface   *other      = create_other();
struct Var_Members_Interface *member     = (struct Var_Other_Interface *)other;
struct Var_Other_Interface   *back_again = (struct Var_Other_Interface)member;

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

Подобного рода вещи не для умственного сердца: вы должны быть очень осторожны с распределением, вложением структур и т. Д.

Посмотрите на старый школьный виджет Xt, и вы поймете идею; Виджеты Xt обычно реализовывались в трех файлах: исходный файл C, открытый заголовок с интерфейсом функции и закрытый заголовок для определения структуры структуры (этот нужен для создания подклассов).

Например, виджет Ghostscript, который я использовал в mgv, выглядел так:

typedef struct {
    /* Bunch of stuff. */
} GhostviewPart;

typedef struct _GhostviewRec {
        CorePart      core;
        GhostviewPart ghostview;
} GhostviewRec;

CorePart был стандартным определением виджета Xt, а GhostviewRec был самим фактическим виджетом.

1 голос
/ 07 апреля 2011

Сначала нужно понять, что такое struct.

Структура в C немного больше, чем стандарт для интерпретации байтов в памяти.

Чтобы увидеть, что это значит, давайте используем вашу структуру:

struct Var_Members_Interface
{
    int intMember;
    char *charMember;
};

struct Var_Members_Interface instance; //An instance of the struct

Что это означает: «Я зарезервирую некоторую память и назову это instance, и я интерпретирую первые несколько байтов, чтобы обозначить целое число, а следующие несколько байтов, чтобы указать, что точка где-то в памяти . "

Учитывая это, не имеет смысла иметь структуры "variable-member" , потому что структура - это просто спецификация макета для существующего блока памяти - и существующих блоков не имеет «переменной» длины.

1 голос
/ 07 апреля 2011

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

0 голосов
/ 07 апреля 2011

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

struct VM_Base
{
    int   intMember;
    char *charMember;
};

struct VM_Variant1
{
    struct VM_Base base;
    int   foo;
};

struct VM_Variant2
{
    struct VM_Base base;
    char *charMember;
    double bar;
};

struct VM_Variant3
{
    struct VM_Base base;
    char   *charMember;
    char baz[10];
};

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

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

struct VM_Variant1
{
    struct VM_Base base;
    int   foo;
};

struct VM_Variant2
{
    struct VM_Base base;
    char *charMember;
    double bar;
};

struct VM_Variant3
{
    struct VM_Base base;
    char   *charMember;
    char baz[10];
};

struct VM
{
    int   intMember;
    char *charMember;
    union
    {
        struct VM_Variant1 vm1;
        struct VM_Variant2 vm2;
        struct VM_Variant3 vm3;
    }
};

Этот второй метод устраняет необходимость в приведении типов. Вы получаете доступ к членам, как это:

double aDouble = aVMStruct.vm2.bar;

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

0 голосов
/ 07 апреля 2011

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

struct Var_Members_Interface
    {
        int intMember;
        char *charMember;
        void *otherMembers;
    };

Редактировать : Решение в этой статье может быть намного ближе к тому, что вы ищете.

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