Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: безымянные struct и union
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
ARV
расширение GNU для GCC вводит возможность задавать в структурах безымянные поля типа struct или union, например, так:
Код
typedef struct {
  uint8_t fix;
  union {
      uint8_t param;
      void *ptr;
   };
}flex_struct_t;

flex_struct_t rec;
и потом в зависимости от условий обращаться rec.param или rec.ptr, как будто эти поля родные для структуры. все хорошо (если не считать того, что Eclipse такой записи не понимает и не подсказывает при вводе имена этих объединенных полей), пока не начинаешь запихивать такие структуры во flash:
Код
PROGMEM flex_struct_t fixed = {.fix=0, .param = 123};
приводит к ошибке unknown field 'param' specified in initializer

вопрос, как обычно, традиционный: что делать? это баг или фича?
MrYuran
Цитата(ARV @ Jul 28 2010, 11:31) *
все хорошо (если не считать того, что Eclipse такой записи не понимает и не подсказывает при вводе имена этих объединенных полей), пока не начинаешь запихивать такие структуры во flash:
Код
PROGMEM flex_struct_t fixed = {.fix=0, .param = 123};
приводит к ошибке unknown field 'param' specified in initializer

А если запихивать не в флешь?
При обращении к полям нет ошибок?

Если дать имя не только типу, но и структуре, то эклипс наверно начнёт "видеть" её поля.
По крайней мере, в С::В именно так.
dimka76
а если попробовать проинитить так

Код
PROGMEM flex_struct_t fixed = {0,  123};
ARV
если инициализировать переменную - ошибка все равно есть. если просто в программе работать с полями - все нормально.

не понимаются именно инициализаторы... просто для настоящих переменных это не принципиально, т.к. всегда можно поля ручками проинициализировать, а для flash альтернатив нету...
MrYuran
А если так:
Код
PROGMEM flex_struct_t fixed = {.fix=0, {.param = 123}};
ARV
Цитата(dimka76 @ Jul 28 2010, 11:49) *
а если попробовать проинитить так

Код
PROGMEM flex_struct_t fixed = {0,  123};

так можно, но выдается warning. зато так непонятно, что к чему инициализируется - от этого и хочется уйти

Цитата(MrYuran @ Jul 28 2010, 11:54) *
А если так:
Код
PROGMEM flex_struct_t fixed = {.fix=0, {.param = 123}};
В ТОЧКУ! спасибо!

странно, что все-таки для обращения к полю не надо писать что-то типа rec..param - понимает же GCC, что поля надо достать из union-а...
dimka76
Цитата(ARV @ Jul 28 2010, 11:57) *
Цитата(dimka76 @ Jul 28 2010, 11:57) *

Код
PROGMEM flex_struct_t fixed = {0,  123};

так можно, но выдается warning. зато так непонятно, что к чему инициализируется - от этого и хочется уйти


а что за предупреждение ?
IgorKossak
QUOTE
... Eclipse такой записи не понимает и не подсказывает при вводе имена этих объединенных полей ...

Helios понимает и всё прекрасно показывает.
Попробуйте покопаться в Preferences->C/C++->Editor->Content Assist->Advanced
ARV
Цитата(IgorKossak @ Jul 28 2010, 13:00) *
Helios понимает и всё прекрасно показывает.
Попробуйте покопаться в Preferences->C/C++->Editor->Content Assist->Advanced
увы... не показывает у меня, и настройки не помогают.

но, что гораздо хуже, я все равно в тупике с инициализаторами
Код
typedef struct {
    prog_char    *text;
    uint8_t        type;
    union{
        struct {
            union{
                uint8_t id;
                int        *num;
            };
            func_ptr    func;
        };
        struct {
            uint8_t        vcnt;
            uint8_t        *vid;
            prog_char    **var;
        };
        _tm    submenu;
    };
} menu_item_t;

#define simple_item(t,i,f)    {.text = t, .type = i_simple, {{{.id=i}, .func = f}}}
#define int_item(t,n,f)        {.text = t, .type = i_int, {{{.num = n}, .func = f}}}
#define var_item(t,c,vi,v)    {.text = t, .type = i_var,{ {.vcnt=0}} }
#define sub_item(t,s)        {.text = t, .type = i_sub, {.submenu = s}}

PROGMEM menu_t mainmenu = {
        .itemcount = 5,
        .items = {
                simple_item(m1,1,NULL),
                int_item(m2,&varvar,NULL),
                var_item(m3,3,&vi,&vars),
                simple_item(m4,1,NULL),
                simple_item(m5,2,NULL)
        }
};

в этом куске: сначала структура с объединенными полями, затем макросы для инициализации разных вариантов этой структуры, в конце - массив элементов с разными вариантами. так вот, var_item() вызывает ошибку - не найдено поле vcnt - в чем дело, не пойму... все прочие варианты отрабатывают...
MrYuran
что-то у вас странное...
Цитата
#define var_item(t,c,vi,v) {.text = t, .type = i_var,{ {.vcnt=0}} }

Слева у макроса четыре аргумента, а справа используется только один...
ARV
Цитата(MrYuran @ Jul 28 2010, 15:46) *
что-то у вас странное...

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

да не в этом же дело... это я уже всякие упрощения делал, чтобы найти причину - изначально все 4 параметра использовались...
никак не получается задать поле - вот в чем беда!
MrYuran
Что-то вы перемудрили...
Как можно инициализировать все элементы юниона, если они друг на друга наложены?
Писать одно поверх другого?
ARV
почему все? я инициализирую только в одном варианте! внутри охватывающего юниона 2 варианта структуры - инициализирую ИЛИ первый вариант, ИЛИ второй - но не в перемешку!
ARV
Жаль, что все знатоки Си, которых тут немало, не желают подсказать, в чем я не прав, и объяснить, как следует поступить... если компилятор описание структуры считает корректным, если доступ к полям происходит - что не так с инициализацией?
dimka76
Код
union{
                uint8_t id;
                int        *num;
            };


Вы пытаетесь объединить 16-ти битную переменную и 8-битную. Указатель в памяти занимает два байта.

Код
union{
        struct {
            union{
                uint8_t id;
                int        *num;
            };
            func_ptr    func;
        };
        struct {
            uint8_t        vcnt;
            uint8_t        *vid;
            prog_char    **var;
        };


Здесь у вас тоже структуры разного размера
ARV
Цитата(dimka76 @ Jul 29 2010, 14:30) *
Код
union{
                uint8_t id;
                int        *num;
            };


Вы пытаетесь объединить 16-ти битную переменную и 8-битную. Указатель в памяти занимает два байта.

вы не открыли америку - это как раз то, чего я и добиваюсь smile.gif инициализирую я, как вы можете видеть, или поле id, или поле num - но не оба одновременно. и использую я их так же исключительно по одному - в зависимости от контекста. кстати, оба этих поля инициализируются нормально, вопрос в другом - почему следующая структура в юнионе не инициализируется?!
ReAl
Цитата(ARV @ Jul 29 2010, 12:35) *
Жаль, что все знатоки Си, которых тут немало, не желают подсказать, в чем я не прав, и объяснить, как следует поступить... если компилятор описание структуры считает корректным, если доступ к полям происходит - что не так с инициализацией?
Ну вот сразу так "С".
Сами же сказали, что это расширение GNU, в C99 такого нет. Так что все претензии к "знатокам GCC", а "знатоков С" трогать не надо.
Кстати, чужая лень не нравится, но и самому лень было поместить в сообщении минимальный компилирующийся пример, чтобы всякие
Код
typedef struct {
    uint8_t itemcount;
    menu_item_t items[];
} menu_t;
не нужно было дописывать...

На мой взгляд, причина проста.
Пусть есть
Код
typedef union {
    char c;
    double d;
} u_t;

Её инициализировать вот так
Код
u_t foo = { 5.0 };
нельзя. Ну, можно, только результат будет эквивалентен
Код
u_t foo = { .c = (char)5.0 };

Т.е. при инициализации объединения без C99-возможности указания имён полей инициализируется всегда первое поле.
В структуре menu_item_t охватывающий union не имеет имён для полей-структур и инициализация летит в первое поле-структуру, а в этой структуре ну таки нет элемента .cnt.
Если переместить структуры в хвост объединения
Код
typedef struct {
    prog_char    *text;
    uint8_t        type;
    union{
        _tm    submenu;
        struct {
            union{
                uint8_t id;
                int        *num;
            };
            func_ptr    func;
        };
        struct {
            uint8_t        vcnt;
            uint8_t        *vid;
            prog_char    **var;
        };
    };
} menu_item_t;
то и первая структура уже не будет инициализироваться.
ARV
до этого предположения я как бы дошел, но у меня закономерный вопрос: а какого, извините, амперсенда, компилятор понимает, что при обращении надо перебрать все имена полей - и перебирает!!! - а при инициализации надо ограничиваться только первыми? я ведь инициализирую с ЯВНЫМ указанием имен полей!!!
neiver
А это стандарт такой в Си, что можно инициализировать только первый элемент в union.
Об этом можно на RSDN спросить, там любят стандарт обсуждать/цитировать.
А если делать универсальное, гибкое меню, то можно как-то так:

Код
struct ItemHeader
{
    prog_char    *text;
              uint8_t        type;
    void *content;
};

struct Type1Item
{
      union
    {
                   uint8_t id;
                   int *num;
    };
    void (*func);
};


struct Type2Item
{
    uint8_t        vcnt;
    uint8_t        *vid;
    prog_char    **var;
};

PROGMEM Type1Item Item1 =
{
    100, NULL
};

PROGMEM Type2Item Item2 =
{
    50, NULL, NULL
};

PROGMEM char m1[] = {"Hello"};
PROGMEM char m2[] = {"Hello2"};

PROGMEM ItemHeader mainmenu[] =
{
    {m1, 0, &Item1},
    {m2, 1, &Item2},
};


ЗЫ. компилировал в С++ режиме
ReAl
Цитата(ARV @ Jul 29 2010, 14:48) *
я ведь инициализирую с ЯВНЫМ указанием имен полей!!!
Полей. да не тех. Эти поля он ищет в первом элементе охватывающего объединения, так как имя поля этого объединения не указано.
Ну когда допиливали GNU exts до неименованных полей, место с инициализацией union трогать почему-то не стали, оставили по стандарту.
Точнее, одну фишку добавили - "union cast", пример из предыдущего моего сообщения можно переписать так
Код
typedef union {
    char c;
    double d;
} u_t;

u_t foo = (u_t)5.0;
и оно чудненько сообразит проинициализировать double-часть.

Но в данном случае даже вместе с C99 compound literals оно не помогает, где-то задалбывается и литерал не считает константой (впрочем, кажется, по C99 compound literal есть отдельный неименованный временный объект, потому он и не рассматривается как константа). Компилировать с -fms-extensions, нужно для отмеченных мест
Код
typedef struct {
    prog_char    *text;
    uint8_t        type;
    union outer {  /* -fms-extensions */
        struct simplei { /* -fms-extensions */
            union{
                uint8_t id;
                int        *num;
            };
            func_ptr    func;
        };
        struct vari { /* -fms-extensions */
            uint8_t        vcnt;
            uint8_t        *vid;
            prog_char    **var;
        };
        _tm    submenu;
    };
} menu_item_t;

typedef struct {
    uint8_t itemcount;
    menu_item_t items[];
} menu_t;

enum { i_simple, i_int, i_var, i_sub };

#define simple_item(t,i,f)    {.text = t, .type = i_simple, (union outer)(struct simplei){{.id=i}, .func = f} }
#define int_item(t,n,f)        {.text = t, .type = i_int, (union outer)(struct simplei){{.num = n}, .func = f} }
#define var_item(t,c,vi,v)    {.text = t, .type = i_var, (union outer)(struct vari){.vcnt=0} }
#define sub_item(t,s)        {.text = t, .type = i_sub, {.submenu = s}}

Цитата
uni_2.c:53: error: initializer element is not constant
uni_2.c:53: error: (near initialization for ‘mainmenu.items[0].<anonymous>’)
и для остальных элементов то же самое
ARV
что-то не выходит каменный цветок и с -fms-extensions ...
ReAl
Цитата(ReAl @ Jul 29 2010, 15:29) *
Но в данном случае даже вместе с C99 compound literals оно не помогает

Увы.
compound literals удобны, но генерирующийся временный объект не даёт инициализировать статические данные, поэтому тут не проходит.
Если бы не это, то прошло бы. А так выходит ошибка, аналогичная
Код
int i1 = 5;
int i2 = i1; // error: initializer element is not constant


А -fms-extensions нужен только для того, чтобы дать имена типам структур неименованных полей и само по себе оно работает.

p.s. то, что на avrfreaks предложили — на мой взгляд, некрасиво. Хотя, конечно, "работать будет".

Несколько "причесать" предложение с avrfreaks (как-то гарантировать одинаковость структур — неименованной и вспомогательной) это вот -fms-extensions таки поможет
Код
typedef struct {
    union{
        uint8_t id;
        int        *num;
    };
    func_ptr    func;
} simple_t;

typedef struct {
    uint8_t        vcnt;
    uint8_t        *vid;
    prog_char    **var;
} var_t;

typedef struct {
    prog_char    *text;
    uint8_t        type;
    union {
        simple_t;  // это неименованные для работы
        var_t;
        _tm    submenu;
        // вспомогательные для инициализации
        simple_t simple_helper; // первая не нужна, но для однообразия пусть будет
        var_t var_helper;
    };
} menu_item_t;


Но всё равно как-то… Хотя "будут, будут одинаковые структуры одинаково расположены", но "некрасиво".
ARV
на avrfreaks в очередной раз посоветовали написать свой компилятор... sad.gif
я все-таки не пойму: это просто недоделка расширения Си или так и положено? написано, что это расширение "для совместимости с некоторыми другими компиляторами" - так вот, в других компиляторах (интересно, каких?) возможна инициализация произволно заданных полей безымянных юнионов ?

я так понимаю, смысл всей этой заморочки в том, чтобы вместо var.struct1.union1.struct2.union2.field писать просто var.field - а все helper-ы и тому подобное (через указатели на произвольные области) - это совсем не то, так как если я поименую часть юнионов, то и обращаться к ним надо будет уже только через имя...
раздразнили сначала коротким вариантом, а потом выясняется, что это полуобман... уж лучше б и не упоминали о такой возможности - не так обидно было бы...
ARV
все-таки на avrfreaks подали идею, как выкрутиться из положения... костыль, но вполне элегантный smile.gif делается еще один или сколько надо именованных юнионов с теми же самыми полями и инициализируются именно именованные юнионы, а последующее обращение ведется через безымянные smile.gif
ReAl
Цитата(ARV @ Jul 30 2010, 21:10) *
все-таки на avrfreaks подали идею, как выкрутиться из положения... костыль, но вполне элегантный smile.gif делается еще один или сколько надо именованных юнионов с теми же самыми полями и инициализируются именно именованные юнионы, а последующее обращение ведется через безымянные smile.gif

Ну да, я выше "причесать" (дать typedef-ы, чтобы не копировать структуры и иметь гарантированное совпадение безымянных для обращения и именованных для инициализации) именно об этом их предложении и написал:
http://electronix.ru/forum/index.php?s=&am...st&p=791927
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.