реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Гибкий размер типа данных
jcxz
сообщение Jul 29 2015, 05:33
Сообщение #1


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Не знаю в какой раздел форума написать, пишу сюда.
Хочется иметь тип данных гибкого размера, с размером, зависящим от значения некоего define.
Т.е. - например:
VAL_X_MAX - максимальное значение, которое может принимать некоторая переменная x.
если: 0 <= VAL_X_MAX < 256, то переменная x должна объявиться размером == байт;
если: 256 <= VAL_X_MAX < 32768, то переменная x должна объявиться размером == 16 бит;
и т.п.

Попытался сделать что-то типа:
Код
#define u8pVOID  u8
#define u16pVOID u16
#define u32pVOID u32
#define u64pVOID u64
#define s8pVOID  s8
#define s16pVOID s16
#define s32pVOID s32
#define s64pVOID s64
#define flexType_subst3(x) x
#define flexType_subst2(x) flexType_subst3(x)
#define flexType_subst(prefix, maxVal, suffix, ...) \
  flexType_subst2(                                  \
  ((maxVal) < B8) ? prefix##8##suffix:   \
  ((maxVal) < B16) ? prefix##16##suffix: \
  ((maxVal) < B32) ? prefix##32##suffix: prefix##64##suffix))
#define flexType(...) flexType_subst(__VA_ARGS__, pVOID)

чтобы можно было объявить переменную:
static flexType(u, 54) x;
где: 54 - макс. значение которое может принимать x
Но препроцессор упорно не хочет полностью вычислять выражение и доводить его до u8 crying.gif
останавливается на:
static ((54) < 0x00000100) ? u8: ((54) < 0x00010000) ? u16: ((54) < B32) ? u32: u64) x;

Интересно - есть-ли какие другие способы?
Конечно можно с помощью #if/#else/#endif, но эту конструкцию придётся городить для каждой такой переменной, а хотелось бы 1 раз определить макрос, а потом его использовать, записывая объявления кратко.

ЗЫ: Компилятор - IAR for ARM 7.20.
Go to the top of the page
 
+Quote Post
scifi
сообщение Jul 29 2015, 05:48
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136



Цитата(jcxz @ Jul 29 2015, 08:33) *
чтобы можно было объявить переменную:
static flexType(u, 54) x;

Что за глупости? Только безумец не знает, что число 54 можно хранить в 8-битной переменной. Зачем для этого хитрый макрос?
Ну а если в порядке изучения языка Си в плане возможностей препроцессора, то нет, ничего не получится. Читайте книжки, в которых написано, как работает препроцессор. Там всё просто и понятно.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jul 29 2015, 07:05
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(scifi @ Jul 29 2015, 11:48) *
Что за глупости? Только безумец не знает, что число 54 можно хранить в 8-битной переменной. Зачем для этого хитрый макрос?

Блин...
Код
#define MAX_EVENTS 54  //кол-во обрабатываемых событий
static flexType(u, MAX_EVENTS) x;
Так понятнее?
Чтобы потом, если вдруг потребуется изменить кол-во обрабатываемых событий, не потребовалось лазить по всему коду и менять размер всех переменных, размерность которых зависит от MAX_EVENTS.
И конечно имеются массивы типа flexType(u, MAX_EVENTS)
Go to the top of the page
 
+Quote Post
scifi
сообщение Jul 29 2015, 07:15
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136



Цитата(jcxz @ Jul 29 2015, 10:05) *
Чтобы потом, если вдруг потребуется изменить кол-во обрабатываемых событий, не потребовалось лазить по всему коду и менять размер всех переменных, размерность которых зависит от MAX_EVENTS.

Это всё достигается стандартными средствами. Для того чтобы не требовалось лазить по коду и менять, придуманы макросы и typedefы:
Код
typedef uint8_t event_type;

Если хочется минимальный размер типа, то достаточно добавить assert:
Код
assert_static(MAX_EVENT < (1UL << (8 * sizeof(event_type)))); // проверить, что размер типа достаточный
assert_static(MAX_EVENT >= (1UL << (8 * (sizeof(event_type) / 2)))); // проверить, что размер не избыточный

Если сработает assert, то достаточно будет скорректировать код в одном месте (typedef). Ничего страшного, руки не отвалятся.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jul 29 2015, 08:05
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(scifi @ Jul 29 2015, 13:15) *
Если хочется минимальный размер типа, то достаточно добавить assert:
Код
assert_static(MAX_EVENT < (1UL << (8 * sizeof(event_type)))); // проверить, что размер типа достаточный
assert_static(MAX_EVENT >= (1UL << (8 * (sizeof(event_type) / 2)))); // проверить, что размер не избыточный

Если сработает assert, то достаточно будет скорректировать код в одном месте (typedef). Ничего страшного, руки не отвалятся.

Ну да, можно так. Но хочется чтобы всё автоматом собиралось само. У меня есть сейчас массив структур, в котором несколько элементов могут хранить некие значения. Максимальное значение для
каждого из них задаётся соответствующим define. Все они около 100..200 сейчас. Но завтра возможно придётся один или несколько из них сделать больше.

PS: Из реальных вариантов пока вижу только кувалдой в лоб - прописать массив:
Код
#define FLEX0 u8
#define FLEX1 u8
#define FLEX2 u8
...
#define FLEX255 u8
#define FLEX256 u16
#define FLEX257 u16
...
#define FLEX65535 u16
#define flexType_subst(maxVal) FLEX##maxVal
#define flexType(maxVal) flexType_subst(maxVal)

biggrin.gif
На 8 и 16 бит хватит. Если компилятор потянет такое кол-во констант lol.gif
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jul 29 2015, 10:01
Сообщение #6


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



На c++ это делается просто. Вот ссылка по теме. (Там и на си есть решение, но только для типов не меньше int).


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
dxp
сообщение Jul 29 2015, 13:59
Сообщение #7


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



На плюсах можно копать в сторону templates и traits. Несколько лет назад подобное (подобное - в смысле, генерация типа в зависимости от условий) делал для оптимизации типа, передаваемого в функцию. Там если размер передаваемого объекта был меньше 4 байт, то передавался обычный тип (а объект - по значению), а если больше - то константная ссылка. Подробностей не помню. Но, в общем, traits как раз для подобных целей и предназначены.


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jul 31 2015, 02:36
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(dxp @ Jul 29 2015, 19:59) *
На плюсах можно копать в сторону templates и traits.

Хотелось-бы обойтись без плюсов и их шаблонов. Силами си-препроцессора.
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Jul 31 2015, 05:36
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



напишите свой препроцессор, который перед билдом будет исходники шерстить и подменять слова - типы. Можете написать его на Сsm.gif

Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Jul 31 2015, 05:46
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



При написании меню мне понадобилось статически менять тип переменной. Для инициализации.
Задача не совсем Ваша, но может посмотрите и что-то заимствуете ...
Объявление:
Код
typedef union
{
  int32_t    i;
  float        f;
} variant_t;

Применение
Код
DigEditing_t dgeFreqRtcCorr =            // Меню "Коррекция генератора"
{
  (int32_t const*)&RtcTCorr,                // прямое
  0,                                        //
  0,                                        // неотображать знак
  10, 4,                                    // 10 знака, 4 знак после запятой
  3,                                        // float
  {.f = 1952.2}, {.f = 1954.0},                // min,max
  PRGEDIT, SetRtcFreq,                        // Редактировать по PRG, сохранять с помощью SetRtcFreq
  &strMks                                    // "mks"
};


И примерно аналогично... ещё пример приведу ... в бутлоадере
Правда не я писал
Код
typedef union
{
    struct
    {
        const uint8_t high;
        const uint8_t low;    
        const VersionDate date;
    };

    struct
    {
        volatile uint8_t high_;
        volatile uint8_t low_;    
        volatile VersionDate date_;
    };

    const uint32_t         value;
    volatile uint32_t     value_;
} Version;
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Jul 31 2015, 06:45
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



А что кстати дает такой тип кроме путаницы и зоопарка дефайнов?

в чем разница между


Цитата
#define MAX_EVENTS 54 //кол-во обрабатываемых событий
static flexType(u, MAX_EVENTS) x;


и

Код
typedef int8_t MY_TYPE;
static MY_TYPE x;


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

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


Ну и опять же если все структуры и все плавает по размерам, съезжают протоколы, ИМХО только путаница....




Цитата
При написании меню мне понадобилось статически менять тип переменной. Для инициализации.

чет между объявлением и применением я не нашел корреляции %(
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Jul 31 2015, 07:51
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Цитата(Golikov A. @ Jul 31 2015, 09:45) *
чет между объявлением и применением я не нашел корреляции %(

Давайте упростим.
Объявлен следующий тип:
Код
typedef union
{
  int32_t    i;
  float        f;
} variant_t;

Далее объявляется следующая переменная
Код
const variant_t        min = {.f = 1952.2};

То есть на этапе инициализации выбирается её тип.

Я не проверял что будет, если я объявлю например данную конструкцию
typedef union
{
int8_t i8;
int16_t i16;
int32_t i32;
float f;
} variant_t;

а при объявлении скажем сделаю
const variant_t min = {.i8 = 19};

Сколько будет выделено памяти?
Скорее всего будет выделено по максимуму.
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Jul 31 2015, 08:22
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



понятно, ну тогда можно и так

char AbstractData[8];

*((int *)AbstractData) = 10;
*((float *)AbstractData) = 15.43;
*((char *)AbstractData) = 'a';

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


кстати вот здесь
Цитата
const variant_t min = {.i8 = 19};

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

такого рода заморочки в структурах надо конечно через void * решать. делать указатель воид, и по какому то признаку правильно к нему обращаться,а юнионы тут излишни ИМХО
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jul 31 2015, 10:14
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Golikov A. @ Jul 31 2015, 11:36) *
напишите свой препроцессор, который перед билдом будет исходники шерстить и подменять слова - типы. Можете написать его на Сsm.gif

biggrin.gif
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jul 31 2015, 10:28
Сообщение #15


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(Golikov A. @ Jul 31 2015, 11:22) *
понятно, ну тогда можно и так


char AbstractData[8];

*((int *)AbstractData) = 10;
*((float *)AbstractData) = 15.43;
*((char *)AbstractData) = 'a';
И во-первых огрести все проблемы с выравниванием на отличных от AVR архитектурах, а во-вторых - загадить исходник явными приведениями типов. Тяжелое ассемблерное детство...
Цитата(Golikov A. @ Jul 31 2015, 11:22) *
такого рода заморочки в структурах надо конечно через void * решать.
Почему именно так? Чем это лучше варианта с union (намекаю на выравнивание и загаживание исходников явными приведениями)? А если надо хранить uint64_t, когда указатель на void занимает 32 бита?


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th July 2025 - 07:25
Рейтинг@Mail.ru


Страница сгенерированна за 0.015 секунд с 7
ELECTRONIX ©2004-2016