Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Поиск по массиву в compile time на С
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2
amaora
Есть массив регистров, ссылки на переменные с именами и некоторыми параметрами, для удобного доступа по разным интерфейсам и конфигурирования устройства. Допустим вот так объявленный,

Код
typedef struct {

    const char        *sym;

    const char        *fmt;
    int            mode;

    union {

        float        *f;
        int        *i;
    }
    link;

    void            (* proc) (const void *reg, void *lval, const void *rval);
}
reg_t;

#define REG_E(link, extra, unit, fmt, mode, proc)    { #link extra "\0" unit, fmt, mode, \
                            { (void *) &link }, (void *) proc }

#define REG(link, unit, fmt, mode, proc)    REG_E(link, "", unit, fmt, mode, proc)

const reg_t        regfile[] = {

    REG(hal.USART_baud_rate,        "",    "%i",    REG_CONFIG, NULL),
    REG(hal.PWM_freq_hz,            "Hz",    "%i",    REG_CONFIG, NULL),
    REG(hal.PWM_dead_time_ns,        "ns",    "%i",    REG_CONFIG, NULL),
    REG(hal.ADC_reference_voltage,        "V",    "%3f",    REG_CONFIG, NULL),
    REG(hal.ADC_current_shunt_resistance,    "Ohm",    "%4e",    REG_CONFIG, NULL),
...
    REG(pm.probe_speed_ramp,    "rad/s",    "%2f",    REG_CONFIG, NULL),
    REG_E(pm.probe_speed_ramp, "_rpm",    "rpm",    "%2f",    REG_NORMAL, &reg_proc_rpm),
...


Регистры адресуются по именам или по номерам. Имена достаточно постоянны, а номера могут изменяться в следующих версиях. В этом проблема, нельзя в коде просто взять ссылку на регистр по номеру regfile[n], номер может измениться. Искать по имени в compile time слишком сложно, средствами C и препроцессора не обойтись. По адресам переменных нельзя, они могут повторятся. Какие еще варианты?

Сейчас думаю, генерировать из объявления массив макросов вида #define REG_hal_USART_baud_rate 0, но здесь тоже много проблем, надо как-то "спасать" недопустимые символы (точка и др.).

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

Спасибо.


Kabdim
А зачем так сделано? Выглядит как кусок для внешнего скриптового языка. Но судя по описанию это не так.
Forger
Масло-масляное.
Ваш некий реестр (я так понял по названию - константный, т.е compile-time), тогда все это можно сделать через некий h-файл, где через #define описать все параметры проекта.
Тогда нет никакого смысла создавать некий глобальный константный массив-реестр, ведь индексам этого массива все равно придется дать осмысленные имена, т.е. плодить те самые #define.

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

Пример содержимого GlobalSettings.h (Keil), (все "лишнее" вырезал, чтобы не плодить новые вопросы)
Код
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Project:        STM32L Bootloader
// Description:    Project settings
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Use Configuration Wizard in Context Menu
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// ================================================= SYSTEM ====================================================
//    <h>System

//     <o>Core Frequency [MHz]
//        <24=> 24 MHz
#define CORE_FREQUENCY_MHZ                        (24)
#define CORE_FREQUENCY_HZ                            (CORE_FREQUENCY_MHZ * 1000000UL)

//     <o>HSE Quartz Frequency [MHz]
//        <12=> 12 MHz <8=> 8 MHz
#define HSE_FREQUENCY_MHZ                            (12)

//    <o>Interrupts Stack size [bytes] <128-65536>
//    <i> Defines the stack size for all interrupts (incl. nested)
//    <i> Minimum: 128
#define INTERRUPTS_STACK_SIZE_BYTES                    (512)

//    </h>

// ============================================== APPLICATION ==================================================
//    <h>Application

//    <s.32>Application Name
#define APPLICATION_NAME                            "******"

//    <s.32>CPU Chip Name
#define CPU_CHIP_NAME                                "STM32L151RCT6A"

//    <h>Bootloader version:
//    <o>Major
#define BOOTLOADER_VERSION_MAJOR                    (1)

//    <o>Minor
#define BOOTLOADER_VERSION_MINOR                    (0)

//    <o>Build
#define BOOTLOADER_VERSION_BUILD                    (1)

//    </h>

//    <h>Hardware version:
//    <o>Major
#define HARDWARE_VERSION_MAJOR                        (1)

//    <o>Minor
#define HARDWARE_VERSION_MINOR                        (2)

//    </h>

//    </h>


.........


Вот так выглядит доступ к этим настройкам этого h-файла в Keil:

Нажмите для просмотра прикрепленного файла




зы. А вот для динамических (сохраняемых настроек), которые могут изменяться в процессе работы приложения, я использую отдельный модуль Settings, который дает интерфейс к доступу к ряду параметров.
Причем, не реализуется просто доступ по некому абстрактному индексу в неком мифическом массиве, а по названию, т. е. через соотв. функции, чтобы ибежать непреднамеренно порчи "чужих" параметров - "реестр" нужно защищать, это вам не винда wink.gif
amaora
Параметры не константные, все разные, есть настройки сохраняемые во флеш, есть текущие параметры работы которые можно только прочитать, и другие типы регистров (виртуальные, для доступа к одним и тем же данным разными способами). Такое описание позволяет предоставить доступ ко всем параметрам, через командный интерфейс по USART, через CAN и тд. Модуль сохранения настроек во флеш тоже пользуется готовым списком.

Основной код работающий с этими параметрами использует свои структуры (вот на них и ссылаются регистры) и не знает про массив regfile. Но в некоторых местах хочется попользоваться описанием регистров, для форматированного вывода с указанием единиц измерения например. Вот вопрос как получить ссылку на нужный регистр. Чтобы и в run time не делать много работы (ведь регистр там будет нужен всегда один и тот же), и в то же время не поломать ссылку когда измениться порядок/состав массива regfile.

То есть сейчас у меня в коде вот так.

Код
printf("R %4e (Ohm)" EOL, &pm.const_R);


А хочется вот так.

Код
reg_print_fmt(REG_pm_const_R);

Kabdim
А точка в ваших определениях регистров всегда будет одна? Хотя наверное можно сделать и с несколькими.

Нбросал пример на Х макросах:
Код
#define REG_LIST                                    \
    X(hal, USART_baud_rate,        "",    "%i",    REG_CONFIG, NULL) \
    X(hal, PWM_freq_hz,            "Hz",    "%i",    REG_CONFIG, NULL) \
    X(hal, PWM_dead_time_ns,        "ns",    "%i",    REG_CONFIG, NULL) \
    X(hal, ADC_reference_voltage,        "V",    "%3f",    REG_CONFIG, NULL) \
    X(hal, ADC_current_shunt_resistance,    "Ohm",    "%4e",    REG_CONFIG, NULL) \
...
    X(pm, probe_speed_ramp,    "rad/s",    "%2f",    REG_CONFIG, NULL) \
    X(pm, probe_speed_ramp, "_rpm",    "rpm",    "%2f",    REG_NORMAL, &reg_proc_rpm) \
    
#define X(a, b, c, d, e, f) REG((a ## . ## b), (c), (d), (e), (f)),
const reg_t        regfile[] = {
    REG_LIST
};
#undef X

#define X(a, b, c, d, e, f) REG_ ## a ## _ ## b,
typedef enum {
    REG_LIST
} regfile_enum;
#undef X


Могут быть шероховатости, возможно где-то не будет компилироваться из-за лишней запятой, писал по памяти.
x736C
Цитата(Forger @ May 3 2018, 15:15) *
зы. А вот для динамических (сохраняемых настроек), которые могут изменяться в процессе работы приложения, я использую отдельный модуль Settings, который дает интерфейс к доступу к ряду параметров.
Причем, не реализуется просто доступ по некому абстрактному индексу в неком мифическом массиве, а по названию, т. е. через соотв. функции, чтобы ибежать непреднамеренно порчи "чужих" параметров - "реестр" нужно защищать, это вам не винда wink.gif

Как примерно хотя бы выглядит модуль Settings, не могли бы поделиться? И как обеспечивается защита?

И еще вопрос вдогонку.
Допустим, в StaticSettings у меня есть настройка #define FW_REV "v.1.1".
Где-то должен быть, например, const char *rev = FW_REV
Где вы обычно такую информацию храните? в main.c или какой-то отдельный файл, в котором организуется хранение системных констант в памяти?
Прошу прощения за нубские вопросы.
Forger
Цитата(x736C @ May 3 2018, 17:01) *
Как примерно хотя бы выглядит модуль Settings, не могли бы поделиться?

Он написан на плюсах и представляет собой набор методов для доступа к нужным параметром.
Список методов в разных проектах различный, но основной принцип неизменный.
В конструкторе - чтение сохраненных параметров. Если они "битые" (проверяется контрольная сумма), то восстанавливаются параметры по-умолчанию.
Сами рабочие параметры хранятся в ОЗУ, при сохранении они переписываются в энергонезависимую память через отдельную функцию/метод отложенно или сразу при изменении параметра (зависит от проекта).
Пишутся только те параметры, которые изменились. Разумеется, пересчитывается контрольная сумма и тоже переписывается.

Цитата
И как обеспечивается защита?

Один метод = один параметр. (в терминологии С++ "метод", в терминологии С - функция).
Внутри реализации конкретного метода проверяются входные данные.
Более сложные случае реализуются несколько иначе.

Цитата
И еще вопрос вдогонку.
Допустим, в StaticSettings у меня есть настройка #define FW_REV "v.1.1".
Где-то должен быть, например, const char *rev = FW_REV

Сами параметры хранятся естественно в виде одной большой структуры (struct), т.е. никаких элементов некого мифического массива.
Доступ к полям этой структуры ИСКЛЮЧИТЕЛЬНО через методы (функции).

В StaticSettings я объявляю рабочие характеристики проекта, которые в процессе работы никогда не меняются, а также даю значения параметрам по-умолчанию (default) для модуля Settings.

Т.е. на голом С я бы создал в Settings.c ОДИН экземпляр структуры всех параметров (в ОЗУ) с квалификатором static, разумеется.
В Settings.h объявил бы кучу функций с ОСМЫСЛЕННЫМИ именами.

Цитата
Где вы обычно такую информацию храните?
Это не принципиально, но чаще всего в EEPROM памяти, если нет, то во FLASH.

Цитата
в main.c или какой-то отдельный файл, в котором организуется хранение системных констант в памяти?

Нет, все это сделано внутри файла Settings.cpp

Почему так делаю? Да потому что принципиально не использую глобальные переменные. От слова вообще, поэтому подобные данные хранятся внутри класса Setting.

Все остальные части (у меня модули) проекта понятия не имеют о существовании некого Setting (равно как и других модулей), т.е. Settings.hpp инклудится только в одном месте и виден только классу Application.
А для доступа к методам класса Settings используются "делегаты", а Application лишь связывает модули вместе, точнее связывает их делегаты.
Впрочем, это уже совсем другая история и к этой теме не имеет отношения.

зы. Я описал чисто свой способ реализации этой задачи - мне ТАК проще. Поэтому прошу не разводить на базе моих выкладок очередной холивар на тему C vs C++.
amaora
Функции доступа я тоже мог бы написать, но в этом нет смысла, доступ я получаю напрямую к переменным (pm.const_R). А регистр мне нужно найти для того, чтобы вытащить дополнительную информацию об этой переменной, формат вывода и единицы измерения.

Пока только вот такое решение вижу, это поиск по строке в run time. Недостатки, медленно, некрасиво, и ошибки (опечатка в строке) буду выявлены только при выполнении этого кода.

Код
reg_print_fmt(reg_search_by_name("pm.const_R"));


Цитата(Kabdim @ May 3 2018, 16:40) *
А точка в ваших определениях регистров всегда будет одна? Хотя наверное можно сделать и с несколькими.
Могут быть шероховатости, возможно где-то не будет компилироваться из-за лишней запятой, писал по памяти.


Точка там не от того, что так захотелось, это же поля структуры. Бывают еще и скобки от индексации массивов. Но суть понятна, объявить список отдельным макросом и развернуть его два раза.
jcxz
Цитата(Forger @ May 3 2018, 15:15) *
В своих проектах я использую такой файл и называю StaticSettings.h
Править параметры проекта в одном файле проще, чем рыться по всему проекту в поисках нужного параметра.

.....и как только над проектом начинают одновременно работать >=2 программиста, начнутся проблемы с совместной модификацией этого мега-файла настроек. laughing.gif
А когда число программистов >=4 начинается вообще полная $опа.
Лучше разбивать настройки по отдельным файлам., принадлежащим разным программным модулям. Естественно - речь о локальных настройках. Глобальных много не должно быть и они должны быть в одном файле. Вобщем - настройки надо делить на группы, по функционалу и не класть всё в одну кучу. И сопровождение будет проще и модификации и использование в других проектах.
Forger
Цитата(jcxz @ May 5 2018, 12:33) *
Лучше разбивать настройки по отдельным файлам., принадлежащим разным программным модулям. Естественно - речь о локальных настройках. Глобальных много не должно быть и они должны быть в одном файле. Вобщем - настройки надо делить на группы, по функционалу и не класть всё в одну кучу. И сопровождение будет проще и модификации и использование в других проектах.

Согласен, неднократно это обмозговывал, но я работаю в основном один, проекты небольшие, но много. Общая стилистика и подход в таком случае очень оправданы.
Общие константные настройки остались в одном файле, поскольку Keil умеет нормально отображать такие файлы (см. скриншот выше).
Так получает удобнее и нагляднее видеть все на одной странице экрана, все разбито по группам, "лишние" настройки пр нужде "сворачиваются".
К тому же приучил себя и многих коллег к SVN, это дисциплинирует и приучает к порядку sm.gif

А вот привязку пинов в новых проектах уже разнес по соотв. модулям. Это выходит проще (пример):
Код
class PowerController : public Module
{
............

private:

............

    class Thread : public OS::Thread<PowerController, POWER_CONTROLLER_MODULE_STACK_SIZE>
    {
    public:

        inline void powerEnable() { pinPowerEnable.setToHigh(); }
        inline void powerDisable() { pinPowerEnable.setToLow(); }
............
        
    private:

        virtual void initialize();
        virtual void body();

    private:

        Hardware::DigitalOutputPin<PA15>     pinPowerEnable;
        Hardware::DigitalOutputPin<PC13>     pinEnable12V;
        Hardware::DigitalOutputPin<PC6>     pinSlaveEnable;
    
............
    
    } thread;

};
jcxz
Цитата(Forger @ May 5 2018, 13:26) *
К тому же приучил себя и многих коллег к SVN, это дисциплинирует и приучает к порядку sm.gif

SVN хорошо, когда каждый работает над своей группой файлов и общих файлов (модифицируемых разными людьми) как можно меньше.
Потому, что например когда Петя пишет/отлаживает модуль SPI-FLASH и по ходу работы правит ключи ему принадлежащие, в этот момент Вася закончил отладку CAN и фиксит его на SVN (со своей версией общих файлов). А Петя уже поменял ряд констант в общем файле и не факт, что окончательно (а может у него вообще несколько вариантов драйвера с разными алгоритмами работы (ногодрыг/прерывания/DMA/безвестная_либа_надыбанная_в_нете). И он тестит все эти варианты. И теперь ему ещё нужно накатить все изменения с SVN на все эти варианты (каждый со своими изменениями в общих файлах!).
А рядом с Васей сидит Коля, который пишет прикладной уровень рабочего протокола обмена (тоже внеся свои изменения в общие файлы). Т.е. - писал, пока его не прервал, Александр Иванович из ГИП-ов, прибежавший в мыле от Заказчика, когда у того вылез очередной Колин баг и система (с нашими девайсами) встала колом. И Коля бросает текущую задачу и ищет баг. И после того как он смастырит костыль, фиксящий данный баг, ему надо вернуться к прерванной задаче, не забыв накатить все изменения общих файлов и разрешив все коллизии накопившиеся за это время из-за своих, Петиных и Васиных commit-ов в SVN. А ведь кроме Александра Ивановича есть и другие ГИП-ы, возможно более авторитетные, в связи с чем уровень рекурсии Коли может быть существенным.
Вобщем - получается куча лишней пустой работы. laughing.gif
Forger
Цитата(jcxz @ May 5 2018, 14:02) *
Вобщем - получается куча лишней пустой работы. laughing.gif

Такая ситуация лишь означает:
- не самое рациональное использование SVN как инструмента,
- недостаточно грамотное проектирование проекта
- неудачное разделение областей ответственности.

Но это все решаемо. Обычно, по ходу работ отлаживается этот механизм. Было бы желание wink.gif
amaora
Вот так сделал wink.gif

Код
cat regfile.c | sed -n 's/^[^#]*\(REG_DEF_\?E\?(.\+)\).*$/\U\1/p' | sed 's/REG_DEF(\([^, ]\+\) *,.\+)/ID_\1,/;s/REG_DEF_E(\([^, ]\+\) *,[^,]*\"\([^, ]\+\)\".\+)/ID_\1\2,/;s/\.\|\[\|\]/_/g' > regdefs.h

...

enum {
#include "regdefs.h"
};

...

        reg_print_fmt(&regfile[ID_PM_CONST_LD], 1);
        reg_print_fmt(&regfile[ID_PM_CONST_LQ], 1);


То есть генерирую список констант по исходнику с объявлением массива, и делаю из этого enum.
Arlleex
Цитата(amaora @ Aug 11 2018, 21:54) *
Вот так сделал wink.gif

Код
cat regfile.c | sed -n 's/^[^#]*\(REG_DEF_\?E\?(.\+)\).*$/\U\1/p' | sed 's/REG_DEF(\([^, ]\+\) *,.\+)/ID_\1,/;s/REG_DEF_E(\([^, ]\+\) *,[^,]*\"\([^, ]\+\)\".\+)/ID_\1\2,/;s/\.\|\[\|\]/_/g' > regdefs.h

...

enum {
#include "regdefs.h"
};

...

        reg_print_fmt(&regfile[ID_PM_CONST_LD], 1);
        reg_print_fmt(&regfile[ID_PM_CONST_LQ], 1);


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

Что это за (очень мягко говоря) фигня??? Вы добиваетесь монопольного владения проектом и быть незаменимым сотрудником? Тогда для этого есть куча других способов сломать проект и мозги его поддерживающих в будущем wacko.gif
amaora
Ну, здесь я обычно не задаю вопросов по своей основной работе, это пока всего лишь проект который делаю один в свободное время. Но все таки у меня нет цели написать непонятный код, наоборот я пытаюсь уменьшить его сложность. А подобные задачи у меня возникали уже не раз, и ничего лучше генерации кода сторонними средствами (sed в данном случае) я не нашел. Иначе получится код в котором внося некоторое изменение нужно подправить еще вот здесь и вот там, и следить чтобы каждая часть соответствовала другой.
Nixon
Для доступа по индексу можно так сделать
Код
enum VARIABLES {
    VAR1,
    VAR2,
    ...
    VARN,
    VAR_MAX
};
описание списка переменных
Код
const TVAR VarArray[VAR_MAX] = {
    [VAR1] = <тут ваша реализация>,
    [VAR2] = <тут ваша реализация>,
    ...
    [VARN] = <тут ваша реализация>
};
собственно ваш реестр инициализации.
Обращение к реестру можно делать по индексу (0-N) или по имени (VAR1-VARN).
Forger
Цитата(Nixon @ Aug 13 2018, 16:58) *
Для доступа по индексу можно так сделать...

a14.gif
DASM
Вот чтобы не плодить тем, возник вопрос. Есть структура в вижуал студио. Ее поля имеют тип enum. Эта структура необходима для сериализации по юарту. Заниматься разборкой каждого поля, или, особо извращённым способом, делать каждое поле методом, пишущим себя в выходной поток нет ни желания, ни необходимости. Поэтому аккуратно подставив pragma pack попробовал. Все замечательно, но вот студия считает, что поля типа моего enum имеют тип int, обьявление у меня простое, по шариковски, typedef emum {Eins ,Zwei, Polizei} eMytype; А вот GCC решил, что такой тип прекрасно ложится в чар, на что имеет право
Цитата
Each enumeration defines a type that is different from all other types. Each enumeration also has an underlying type. The underlying type can be explicitly specified using enum-base; if not explicitly specified, the underlying type of a scoped enumeration type is int. In these cases, the underlying type is said to be fixed. Following the closing brace of an num-specifier, each enumerator has the type of its enumeration.
(хотя мой инглиш слаб, и может и не имеет). Теперь мысли что делать. В с++ с типами строже, отказываться от типа энум не хочу, там это ошибка, причем полезная. Можно сделать как typedef emum {Eins ,Zwei, Polizei, empty = 0xffffff}, в Гцц, тогда он сделает энумы интом, но это тоже так себе идея, тем более скорее хотелось бы пыл студии усмирить на предмет делать все это интом. Третий вариант в студии enum class eMytype : char {Eins ,Zwei, Polizei} сделать, но тогда не совсем понял как юзать старый трюк в С с " {Eins ,Zwei, Polize, max_enum_elents}, max_enum_elents будет строго типизировано и пробежаться по его его варинтам вплоть до него "по индексу" не выйдет.

Цитата(Nixon @ Aug 13 2018, 16:58) *
Для доступа по индексу можно так сделать
Код
enum VARIABLES {
    VAR1,
    VAR2,
    ...
    VARN,
    VAR_MAX
};
описание списка переменных
Код
const TVAR VarArray[VAR_MAX] = {
    [VAR1] = <тут ваша реализация>,
    [VAR2] = <тут ваша реализация>,
    ...
    [VARN] = <тут ваша реализация>
};
собственно ваш реестр инициализации.
Обращение к реестру можно делать по индексу (0-N) или по имени (VAR1-VARN).

Симпатично, жаль это since C99, поэтому не так много где описано и использовано. Буду знать.
Forger
Цитата(DASM @ Aug 13 2018, 20:11) *
...

Пытаясь понять этот трудночитаемый набор слов, мне удалось понять мысль - один компилятор трактует enum как char, а другой - как int.
Но это вроде как решаемо: всего лишь в enum нужно явно указать тип хранимых данных.
Примерно так: typedef enum { Eins, Zwei, Polizei, empty = (uint32_t)0xffffff }
DASM
А наоборот как ?
Код
typedef enum {t1, t2, t3 = (char) 100} myE;
#pragma pack (1)
    struct  My
    {
        myE e1;
        myE e2;
        myE e3;
    };
#pragma pack ()
int main()
{
    cout << offsetof(My, e1) << endl;
    cout << offsetof(My, e2) << endl;

}
...... e1 e2 e3 как были int так и остались.
Компилятору по барабану все эти (char), он, как я понимаю, обязан уложиться в int и не более. Попросить его ограничиться char не могу, это compilator depended
Можно было бы как
Код
enum class myE : char {t1, t2, t3, eMAX};
#pragma pack (1)
    struct  My
    {
        myE e1;
        myE e2;
        myE e3;
    } my;
#pragma pack ()
int main()
{
    for (myE i = myE::t1; i < myE::eMAX; i++)

Только вот так не получится , да и понятно почему (второе изображение)
Выходит в С слишком опасно, в С++ слишком неудобно либо неэффективно. Решение есть, вот оно и ищется
Forger
По-ходу я ошибся с явным указанием типа enum, у меня не работает sad.gif
Нужно это делать ключами компилятора: http://www.keil.com/support/man/docs/armcl...11640303038.htm

Кстати, у меня pragma pack вот так используется:
Код
#pragma pack(push, 1)
....
#pragma pack(pop)

Сергей Борщ
QUOTE (DASM @ Aug 13 2018, 20:36) *
max_enum_elents будет строго типизировано и пробежаться по его его варинтам вплоть до него "по индексу" не выйдет.
Явное приведение типа все еще работает. for(uint_fast8_t i = 0;i < uint_fast8_t(eMytype::max_enum_elents); ++i)/
QUOTE (DASM @ Aug 13 2018, 20:36) *
Симпатично, жаль это since C99
18 лет, уже не девочка...
DASM
Цитата(Сергей Борщ @ Aug 13 2018, 21:31) *
Явное приведение типа все еще работает. for(uint_fast8_t i = 0;i < uint_fast8_t(eMytype::max_enum_elents); ++i)/
18 лет, уже не девочка...

да оно и неявно сработает, и в этом случае без enum class "eMytype::max_enum_elents" будет лишним, но нехорошо. Ладно, пусть нехорошо, pragma pack тоже совсем нехорошо. Но как enum сделать гарантированно char ами?
Forger
Цитата(DASM @ Aug 13 2018, 21:37) *
Но как enum сделать гарантированно char ами?

Остается только ключами компилятора
DASM
Цитата(Forger @ Aug 13 2018, 21:25) *
По-ходу я ошибся с явным указанием типа enum, у меня не работает sad.gif
Нужно это делать ключами компилятора: http://www.keil.com/support/man/docs/armcl...11640303038.htm

Кстати, у меня pragma pack вот так используется:
Код
#pragma pack(push, 1)
....
#pragma pack(pop)

я думал, что могут быть такие ключи, но это еще меньше делать хочется. Если я делаю такие мерзости как явное приведение типов, то стараюсь писать static_cast <>, такая многословность в таких случаях в тему, при просмотре кода понимаю, что писав это понимал что делаю и чем чревато. А вот ключ компилятора ( если он есть) будем забыт сразу, в коде не видно. Признаюсь, особенно не мучался ранее стандартами, в голове засело, что enum это int, то ли книги так пишут, что непонятно. На самом деле enum это может и char и short и int быть, и , насколько помню, при сравнении такого enum с другим типом, имеющим неявное преобразование не будет даже warning.. А вот тут споткнулся
Forger
Цитата(DASM @ Aug 13 2018, 21:43) *
А вот тут споткнулся

В "протокольных" делах enum не использую, но очень активно использую union:

CODE
static const struct
{
// Device answers
uint8_t Ok;
uint8_t Unknown;
uint8_t WrongFirmware;
uint8_t WriteError;

// Common commands
uint8_t GetDeviceInfo;
uint8_t DeviceReset;
uint8_t BootloaderActivate;

// Bootloader commands
uint8_t GetFirmwareFitures;
uint8_t ChunkUpload;
uint8_t PageWrite;
uint8_t FirmwareVersionUpdate;

// Secret commands
.....
}
Command =
{
0x00, 0x01, 0x02, 0x03, // Device answers
0xA0, 0xA1, 0xA2, // Common commands
0xE0, 0xE1, 0xE2, 0xE3, // Bootloader commands
...... // Secret commands
};

.....

#if defined ( __CC_ARM ) // Arm Compiler v5
#pragma anon_unions
#endif

#pragma pack(push, 1)

struct DeviceInfo
{
Settings::Version bootloaderVersion;
Settings::Version firmwareVersion;
Settings::Version hardwareVersion;
Settings::SerialNumber serialNumber;
.......
};

struct FirmwareFitures
{
uint32_t baseAddr; // Firmware Base Address
uint32_t memorySize; // Flash memory size (including Bootloader and Firmware regions both)
uint8_t chunkSize;
uint16_t pageSize;
};

struct FirmwareCheckZone
{
uint16_t crc16;
uint16_t sizeKb;
};

......

struct Report // 64 bytes
{
uint8_t command;
union
{
uint8_t payLoad[REPORT_SIZE_BYTES - sizeof(command)];

struct DeviceInfo deviceInfo;

struct FirmwareFitures firmwareFitures;

struct
{
uint32_t addr;
uint8_t data[FIRMWARE_CHUNK_SIZE_BYTES];
} chunk;

struct
{
uint32_t addr;
} page;

...............
};
};

#pragma pack(pop)


Оба кода одинаково работают на Qt5 и под keil (v5 и v6), что очень удобно.
Доп. ключей компиляции не использую.

Использование const struct вместо define и enum (ну, не люблю их) позволяет мне делать код более читаемым.
Разбор пакетов, в которых идут разношерстные данные благодаря union делает его очень компактным и также хорошо читаемым:

CODE

// в rxReport - уже лежит полученный пакет (USB hid)

if (rxReport.command == Command.GetDeviceInfo)
{
txReport.deviceInfo.bootloaderVersion = settings.getBootloaderVersion();
txReport.deviceInfo.firmwareVersion = settings.getFirmwareVersion();
txReport.deviceInfo.hardwareVersion = settings.getHardwareVersion();
txReport.deviceInfo.serialNumber = settings.getSerialNumber();
......

txReport.command = Command.Ok;
sendReport(txReport);
return;
}

if (rxReport.command == Command.GetFirmwareFitures)
{
txReport.firmwareFitures.baseAddr = FIRMWARE_BASE_ADDRESS;
txReport.firmwareFitures.memorySize = FLASH_SIZE_KB * 1024UL;
....
txReport.command = Command.Ok;
sendReport(txReport);
return;
}

if (rxReport.command == Command.DeviceReset)
{
txReport.command = Command.Ok;
sendReport(txReport);
NVIC_SystemReset();
}

if (rxReport.command == Command.BootloaderActivate)
{
settings.killFirmware();
NVIC_SystemReset();
}

if (rxReport.command == Command.SerialNumberChange)
{
settings.setSerialNumber(rxReport.deviceInfo.serialNumber); // Other "deviceInfo" fields are ignored
txReport.command = Command.Ok;
sendReport(txReport);
return;
}

.....

Никаких адских массивов и массы магических чисел в качестве индесов для доступа к его содержимого.
Через полгода/год открою этот код и сразу пойму, что тут происходит.
DASM
Гы, const struct а тут мы возвращаемся к вопросу их инициализации, который вы почему то сочли непонятным
static const struct это к кому относится? Разве что делать отдельный namespace как вариант ? Иначе также самая нелюбимая вами глобальная переменная. Глобальная в одном файле - все равно не отмазка.
Сергей Борщ
QUOTE (DASM @ Aug 13 2018, 21:37) *
да оно и неявно сработает, и в этом случае без enum class "eMytype::max_enum_elents" будет лишним
Неявно сработает только в случае простого enum. В случае enum class нужно явное приведение и "eMytype::max_enum_elents" тоже будет нужно.
QUOTE (DASM @ Aug 13 2018, 21:37) *
Но как enum сделать гарантированно char ами?
enum class eMytype : uint8_t {}.

И еще: char, signed char и unsigned char - три разных типа, поэтому "голый" char нужно использовать только для хранения символов. Для чисел нужно использовать signed char и unsigned char, а еще лучше (u)uint8_t. Если бы у вас был enum eMytype { '1', '2', '3' } - вот тогда бы я к char не придирался.
Forger
Цитата(DASM @ Aug 13 2018, 22:39) *
Гы, const struct а тут мы возвращаемся к вопросу их инициализации, который вы почему то сочли непонятным
static const struct это к кому относится? Разве что делать отдельный namespace как вариант ? Иначе также самая нелюбимая вами глобальная переменная. Глобальная в одном файле - все равно не отмазка.

Command - НЕ глобальная переменная, а статическая константа, которую, кстати, надо будет сделать constexpr....
Вообще, в данном примере показан бут-загручик (!) в одном из старых проектов, а эта структура Command используется прямо внутри соотв. cpp файла.
Сам набор эти команд (Command) имеет смысл делать видимым только внутри этого модуля (в данном случае он называется Communication).
Чтобы не загромождать соотв hpp файл никому не нужным снаружи этим набором команд, то они просто добавлены внутри соотв. cpp файла и им приписано static.
По сути эта const struct - замена enum, видимая только внутри cpp файла, но позволяющая более легко читать текст, благодаря разбиения на поля и подструктуры.

Впрочем, в данный момент, глядя на эти исходники, некоторые мелочи я бы сделал иначе. ...
Но суть не в этом, а в том, что пример я привел для того, чтобы показать как можно обойтись без enion вообще.
DASM
Цитата(Сергей Борщ @ Aug 13 2018, 22:47) *
Неявно сработает только в случае простого enum. В случае enum class нужно явное приведение и "eMytype::max_enum_elents" тоже будет нужно.
enum class eMytype : uint8_t {}.

И еще: char, signed char и unsigned char - три разных типа, поэтому "голый" char нужно использовать только для хранения символов. Для чисел нужно использовать signed char и unsigned char, а еще лучше (u)uint8_t. Если бы у вас был enum eMytype { '1', '2', '3' } - вот тогда бы я к char не придирался.

Какие придирки? Так я и спрашиваю, как сделать перечисление, где каждый элемент это char? Выше выяснили, что так вот просто через enum компиляторонезависимо не выйдет. Завтра поиграю с юнионом по совету.


Цитата(Forger @ Aug 13 2018, 22:49) *
Command - НЕ глобальная переменная, а статическая константа, которую, кстати, надо будет сделать constexpr....
Вообще, в данном примере показан бут-загручик (!) в одном из старых проектов, а эта структура Command используется прямо внутри соотв. cpp файла.
Сам набор эти команд (Command) имеет смысл делать видимым только внутри этого модуля (в данном случае он называется Communication).
Чтобы не загромождать соотв hpp файл никому не нужным снаружи этим набором команд, то они просто добавлены внутри соотв. cpp файла и им приписано static.
По сути эта const struct - замена enum, видимая только внутри cpp файла, но позволяющая более легко читать текст, благодаря разбиения на поля и подструктуры.

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

Да не замена это. Энум нигде не хранится, просто объявление. Можно сделать его на пару миилиардов элементов, а при использовании поле его займет 4 байта. С const struct несколько не то. Надо ещё подумать
Forger
Цитата(DASM @ Aug 13 2018, 23:08) *
Можно сделать его на пару миилиардов элементов, а при использовании поле его займет 4 байта. С const struct несколько не то. Надо ещё подумать

С const struct все то же самое - вы явно создается один экземпляр, а чтобы сделать второй, то нужно дать этой структуре другое имя, но если не будете ее использовать, то любой современный компилятор вырежет ее из финальной прошивки за ненадобностью.
Однако, если уж так сильно волнуетесь за буквально байты экономии FLASH памяти, то просто не засовывайте в const struct содержимое всей "война и мир", а оставляйте только то, что нужно в данном проекте.
Чтобы уйти от соблазна "случайно" создать второй экземпляр этой структуры, то скрывайте ее область видимости по максимуму и забудьте уже про этот typedef ))

Как вариант, объявить эту структуру const и тут же проинициализировать (как я привел в примере выше) прямо внутри одной функции, которая уже разбирая ее, будет вызывать другие функции, которые ничего не будут знать о вашей чудо-структуре.
Nixon
Может ещё кому полезно будет:
В анонимных структурах и union'ах для исключения лишнего уровня вложенности можно не писать название переменной структуры.
Например как в примере с union выше достаточно написать
Код
struct DeviceInfo;
и поля данной структуры будут на уровне самой структуры. Если конечно нет коллизий с другими переменными.
Очень помогает в экономии ресурса клавиатуры sm.gif
Forger
Цитата(Nixon @ Aug 13 2018, 23:31) *
Например как в примере с union выше достаточно написать
Код
struct DeviceInfo;
и поля данной структуры будут на уровне самой структуры. Если конечно нет коллизий с другими переменными.

В данном случае смысл как раз в том, чтобы deviceInfo нужно было явно указывать в коде, давая понять, с чем именно работаем.
А если от этого сознательно отказаться, то по-моему очень сильно возрастает риск запутаться и в итоге залезть куда не нужно.
Подобные "промашки" (сужу по своему опыту) всегда сопровождается одними из самых скверных багов smile3046.gif

А сам факт анонимности union действительно очень полезен, особенно, если union внутри своего уровня всего один. Код становится короче и не нужно думать, как ее назвать cool.gif
Nixon
Я без привязки к вашему примеру. Просто довольно полезная фишка. Особенно если есть множество структур в которые входит одинаковый блок (чисто протокольные штучки типа заголовка и т.д. ) . Многократно копировать этот блок глупо. Вводить лишний уровень вложенности тоже. Помогает подобное анонимное объявление. Заменяет собой лишний #define можно сказать.
Forger
Цитата(Nixon @ Aug 13 2018, 23:49) *
Просто довольно полезная фишка. Особенно если есть множество структур в которые входит одинаковый блок (чисто протокольные штучки типа заголовка и т.д. ) . Многократно копировать этот блок глупо. Вводить лишний уровень вложенности тоже. Помогает подобное анонимное объявление. Заменяет собой лишний #define можно сказать.

Согласен sm.gif

Цитата
Особенно если есть множество структур в которые входит одинаковый блок

Я это сделал несколько иначе (см. тот пример) - одинаковый блок вынес наружу (command), а все остальное разместил внутри анонимного union.
Сергей Борщ
QUOTE (DASM @ Aug 13 2018, 23:08) *
Выше выяснили, что так вот просто через enum компиляторонезависимо не выйдет.
Я устал в который раз повторять: enum class eMyenum : char { t1, y2, t3}. В Стандарте с 11-го года. Можете не верить. Пробуйте черех union.
DASM
Цитата(Сергей Борщ @ Aug 14 2018, 01:49) *
Я устал в который раз повторять: enum class eMyenum : char { t1, y2, t3}. В Стандарте с 11-го года. Можете не верить. Пробуйте черех union.

А я этот вариант написал в самом своем вопросе. Только вот малоудобность его покажу, когда доберусь компа часа через два
DASM
Вот нашел ответ, как удобно пользовать enum class.

В общем хорошее обсуждение https://stackoverflow.com/questions/8498300...th-enum-classes
Все это краткостью записи не отличается, но иначе никак

вроде все идеи, от макросов до boost и создания своего класса, и типизирование шаблона array по этому enem class
Nixon
Цитата(Сергей Борщ @ Aug 14 2018, 01:49) *
Я устал в который раз повторять: enum class eMyenum : char { t1, y2, t3}. В Стандарте с 11-го года. Можете не верить. Пробуйте черех union.

Не обязательно enum class : type. Достаточно enum : type.
DASM
Цитата(Nixon @ Aug 14 2018, 09:14) *
Не обязательно enum class : type. Достаточно enum : type.

О, кстати такой вариант
Код
enum  meE : char {
        E1,
        E2,
        EMAX
};

int main()
{    
    meE e = E1;
}

Избавляет от необходимости писать meE e =meE::E1;
Правда при этом тип такого enum имеет неявное преобразование в int
но зато полностью решает мою проблему underlyng типа в VS как int и не приводит к необходимости модификации кода. Самый четкий и полезный ответ, собственно это на данный момент и было надо
Nixon
Цитата(DASM @ Aug 14 2018, 09:18) *
О, кстати такой вариант
Код
enum  meE : char {
        E1,
        E2,
        EMAX
};

int main()
{    
    meE e = E1;
}

Избавляет от необходимости писать meE e =meE::E1;

Именно. Называется integral type unscoped enum.

Но в программе с большим количеством enum'ов лучше все же enum class или enum в классе (второй менее жесткий по приведению типов, но такой же структуированный).
Kabdim
По факту тип указанный в энуме, года 3 назад работал как надо не везде. У меня выходило что жизнеспособный вариант для портируемого кода без сериализации - форсировать размеру энума в 32 бита "k..Max = UINT32_MAX". Это похоже все понимают единообразно. По крайней мере из того что я пробовал на тех версиях компиляторов, который были года 3 назад, на тех опциях оптимизации которые мне были интересны. Проблемы были с gcc.
DASM
Да, лучше. Но пока не знал об этом - много писанины сделать, оную править теперь с шаблонами, итераторами просто нет времени. На следующий проект конечно буду знать и учту. Спасибо.

Цитата(Kabdim @ Aug 14 2018, 09:32) *
По факту тип указанный в энуме - филькина грамота ни к чему его не обвязывающая. Единственный жизнеспособный вариант для портируемого кода без сериализации - форсировать размеру энума в 32 бита "k..Max = UINT32_MAX". Это похоже все понимают единообразно. По крайней мере из того что я пробовал на тех версиях компиляторов, который были года 3 назад.

вот в варианте Никсона непохоже это на филькину грамоту совсем, работает именно как надо. Уверен можно найти в стандарте описание этого
"3) Opaque enum declaration for an unscoped enumeration must specify the underlying type." это конечно не текст стандарта, но ресурс вполне заслуживающий доверия
https://en.cppreference.com/w/cpp/language/enum
Nixon
В стандарте то стандарте, но не совсем "С", точнее совсем не "С". C++11 это.
Kabdim
Цитата(DASM @ Aug 14 2018, 09:34) *
"3) Opaque enum declaration for an unscoped enumeration must specify the underlying type."

Почему они так написали - понятно, пишут так что бы их поняли без буквоедства, но кмк тут они переборщили с обобщением. В стандарте следующее:
Цитата
For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the
underlying type.
...
Two enumeration types are layout-compatible if they have the same underlying type.

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

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

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

УПД Поправил цитату стандарта, скопировалось неправильно.
DASM
Цитата(Nixon @ Aug 14 2018, 09:39) *
В стандарте то стандарте, но не совсем "С", точнее совсем не "С". C++11 это.

Ну с связи с возможностью удобной интеграции GCC в MS VS посредством VisualGDB я пересел полностью на GCC, для которого и С ++ 11 совсем не новинка. Если поджимают ресурсы - не пользую stl, new и подобное просто, а сами по себе плюсы ничего не кушают, даже виртуальные методы - точно также делались в голом С, посредством таблиц и передачей в функции явного this, так что ничего не теряю с плюсами это точно


Цитата(Kabdim @ Aug 14 2018, 09:49) *
Почему они так написали - понятно, пишут так что бы их поняли без буквоедства, но кмк тут они переборщили с обобщением. В стандарте следующее:

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

это как раз в Сях, без спецификации underlying типа. А Никсон как раз описал возможность в С11 явно указать этот тип, причем в такой форме это не возможность, а обязанность.
PS laughing.gif я таки с вашими новыми знаниями добил майкрософтовский компилер!!
Kabdim
Цитата(DASM @ Aug 14 2018, 10:10) *
это как раз в Сях, без спецификации underlying типа.

Это цитата из ISO/IEC JTC1 SC22 WG21 N3690 Date: 2013-05-15 7.2 Enumeration declarations
DASM
Цитата
typedef enum { tst, tst2, tst3 } test;
#pragma pack (1)
typedef struct
{
test f;
test g;
test h;
} lala;
lala l;
int t = sizeof(l.f); // = 1 OK
int z = sizeof (lala); // = 3 OK
int a = offsetof (lala, g);// = 1 OK
int q = sizeof(tst); // = 4 WTF ??
#pragma pack ()

А что тут не так с sizeof (tst) ? Он 4 стал равен, хотя все остальное по 1, с учетом того что ГЦЦ привел энум к чару
Kabdim
Цитата(DASM @ Aug 16 2018, 09:32) *
А что тут не так с sizeof (tst) ? Он 4 стал равен, хотя все остальное по 1, с учетом того что ГЦЦ привел энум к чару

А чего вы хотите добиться применяя sizeof к значению?
DASM
Цитата(Kabdim @ Aug 16 2018, 17:29) *
А чего вы хотите добиться применяя sizeof к значению?

Просто понять почему он в структуре один, а вот так просто -4
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.