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

 
 
> Гибкий размер типа данных
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
2 страниц V   1 2 >  
Start new topic
Ответов (1 - 21)
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
jcxz
сообщение Jul 31 2015, 10:45
Сообщение #16


Гуру
******

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



Цитата(Golikov A. @ Jul 31 2015, 14:22) *
как я понимаю все метания автора не выделить памяти больше чем надо, А то так все можно было лонгами покрыть и всех делов....

Да, именно так. Имеется программа, в которой множество массивов структур, и массивов массивов структур. Многие члены этих структур - счётчики количества неких событий
(разного типа событий, для каждого определён максимальный диапазон счётчика). Другие члены этих структур - индексы, указывающие на элементы других массивов
(перекрёстные ссылки между массивами). Также есть ещё связные списки, в членах которых тоже есть индексы для перекрёстного связывания.
Заранее точно не известны максимальные размерности для каждого из этих счётчиков (и соответственно индексов).
Есть ещё кольцевые буфера, сложные, с многопозиционными указателями записи, позиции обработчика1 кольца, ..., позиции обработчикаN кольца, чтения (указатели - тоже индексы,
не указатели на память (для экономии размера)). Макс. кол-во элементов кольцевых очередей тоже задаётся дефайнами. И тоже потом может быть изменено.
Соответственно - размер этих массивов и кольцевых буферов будет сильно зависеть от размерности типов, хранящих эти счётчики и индексы.
И если потом вдруг потребуется поменять пару дефайнов, неохота лазить по всему коду и править.

Да в принципе подобные типы данных встречаются частенько и в других программах, только не в таком количестве wink.gif И мне всегда хотелось придумать какой-то способ объявления.
Пока вижу два варианта решения:
1. Для каждого дефайна, задающего макс. значение счётчика событий, городить конструкцию из #if/#elif/#else/#endif
Но получается очень громоздко и трудночитаемо.
2. Реализовать средствами с++. Такое уже делал. Объявлял что-то типа:
#define flexType_subst(prefix, maxVal) class flex##prefix { \
u8 [(maxVal < 256) ? 1: (maxVal < 65536) ? 2: ... ]; };
#define flexType(maxVal) flexType_subst(#maxVal, maxVal)
Объявление переменной:
#define MAX_EVENTS 54
static flexType(MAX_EVENTS) x;
Перегружал в конструируемом классе операторы приведения к типу int и оператор присваивания типа int.
Всё работало.
Но при отладке оптимизация выключена и очень напрягает, что компилятор не хочет инлайнить операторы приведения типа/присваивания, даже когда указываешь явно inline.
С типом, созданным посредством препроцессора, ассемблерный код выглядит гораздо красивее wink.gif
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Jul 31 2015, 11:05
Сообщение #17


Гуру
******

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



Цитата
Тяжелое ассемблерное детство...

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

Цитата
Почему именно так? Чем это лучше варианта с union

по мне как раз
Цитата
загаживание исходников явными приведениями

которое здорово дисциплинирует и лишний раз подтверждает что вы знаете к какому типу вы обращаетесь....


Цитата
А если надо хранить uint64_t, когда указатель на void занимает 32 бита?

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

Цитата
Объявление переменной:
#define MAX_EVENTS 54
static flexType(MAX_EVENTS) x;


хоть убейте не понимаю я разницы с объявлением

Код
typdef int8_t  Array1Type; // это в отдельном заголовке
Array1Type x;   // это в коде


меняется также в 1 месте, там где вы бы
54 заменили на 1000, смените
typdef int8_t Array1Type
на
typdef int16_t Array1Type.

только вы сохраните работающей функцию sizeof(Array1Type) в отличии от sizeof( flexType(MAX_EVENTS) );
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jul 31 2015, 11:17
Сообщение #18


Гуру
******

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



Цитата(SasaVitebsk @ Jul 31 2015, 13:51) *
typedef union
{
int8_t i8;
int16_t i16;
int32_t i32;
float f;
} variant_t;
Сколько будет выделено памяти?
Скорее всего будет выделено по максимуму.

Выделено будет конечно по максимальному члену. А моя цель - экономия ОЗУ.

Цитата(Golikov A. @ Jul 31 2015, 17:05) *
Код
typdef int8_t  Array1Type; // это в отдельном заголовке
Array1Type x;   // это в коде

меняется также в 1 месте, там где вы бы
54 заменили на 1000, смените
typdef int8_t Array1Type
на
typdef int16_t Array1Type.

Вот именно, что в двух местах: отдельно 54 на 1000 и отдельно потом int8_t на int16_t.
Я стараюсь строить код так, чтобы, если есть зависимые значения (b=f(a)), то чтобы при изменении #define a, значение b пересчитывалось компилятором по заданному выражению и не мной вручную.
Так же хотелось бы делать и для типов: я указываю его максимальный размер, а компилятор по этому размеру считает в переменной какого размера его выгоднее всего хранить.
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Jul 31 2015, 11:45
Сообщение #19


Гуру
******

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



а... ну теперь понятно

ну тогда вам надо просто написать 4 #if else и все
типов то всего
8, 16, 32, 64 бита

определяете
#define MAX_EVENTS 54

и через #if ....#else от значения MAX_EVENTS
определяете typdef или #define если тот закривляется

Код
#if MAX_EVENTS > 0x0FFFFFFFF
  #define my_type uint64_t
#else if MAX_EVENTS > 0x0FFFF
  #define my_type uint32_t
#else if MAX_EVENTS > 0x0FF
  #define my_type uint16_t
#else
  #define my_type uint8_t



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

my_type x;
Go to the top of the page
 
+Quote Post
scifi
сообщение Jul 31 2015, 12:42
Сообщение #20


Гуру
******

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



Цитата(Golikov A. @ Jul 31 2015, 14:45) *
и через #if ....#else от значения MAX_EVENTS
определяете typdef или #define если тот закривляется

Семён Семёныч! Почему-то мне это в голову не пришло. Респект a14.gif
Go to the top of the page
 
+Quote Post
jcxz
сообщение Aug 1 2015, 06:03
Сообщение #21


Гуру
******

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



Цитата(Golikov A. @ Jul 31 2015, 17:45) *
и через #if ....#else от значения MAX_EVENTS
определяете typdef или #define если тот закривляется

Ну это понятно. Я о таком ещё в 16-м сообщении писал (вариант 1).
Вот если-бы си позволял конструкцию #if/#endif в макрос запихнуть! sad.gif
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Aug 1 2015, 07:09
Сообщение #22


Гуру
******

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



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

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

делается через промежуточный дефайн

объявляете базовый дефайн
#define MAX_EVENTS 54


от этого дефайна объявляете тип

#if MAX_EVENTS ....
#define MyType int8_t
#else
#define MyType int16_t
и так далее...


а уже от этого типа объявляете счетчики
MyType Counter;


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



эти подмены работают автоматически, вам их надо написать один раз, и запаковать в файл. Громоздко на самом деле не будет, там всего то 4 строчки по числу типов, просто надо сравнивать в нужном направлении, чтобы проверять одну границу. Ну в общем делайте как хотите sm.gif это реально решение и методами языка С
Go to the top of the page
 
+Quote Post

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

 


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


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