|
Гибкий размер типа данных |
|
|
|
Jul 29 2015, 05:33
|
Гуру
     
Группа: Свой
Сообщений: 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 останавливается на: static ((54) < 0x00000100) ? u8: ((54) < 0x00010000) ? u16: ((54) < B32) ? u32: u64) x;Интересно - есть-ли какие другие способы? Конечно можно с помощью #if/#else/#endif, но эту конструкцию придётся городить для каждой такой переменной, а хотелось бы 1 раз определить макрос, а потом его использовать, записывая объявления кратко. ЗЫ: Компилятор - IAR for ARM 7.20.
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 21)
|
Jul 29 2015, 07:05
|
Гуру
     
Группа: Свой
Сообщений: 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)
|
|
|
|
|
Jul 29 2015, 07:15
|
Гуру
     
Группа: Свой
Сообщений: 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). Ничего страшного, руки не отвалятся.
|
|
|
|
|
Jul 29 2015, 08:05
|
Гуру
     
Группа: Свой
Сообщений: 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) На 8 и 16 бит хватит. Если компилятор потянет такое кол-во констант
|
|
|
|
|
Jul 31 2015, 05:46
|
Гуру
     
Группа: Свой
Сообщений: 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;
|
|
|
|
|
Jul 31 2015, 06:45
|
Гуру
     
Группа: Свой
Сообщений: 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. Меняется в одном месте в начале программы, тайпдефов будет столько сколько уникальных типов, ровно столько сколько у вас было бы дефайновых макс_евантов. В тех местах где вы размер задаете не через дефайн, а явно, можно поставить прямой тип. Ну и опять же если все структуры и все плавает по размерам, съезжают протоколы, ИМХО только путаница.... Цитата При написании меню мне понадобилось статически менять тип переменной. Для инициализации. чет между объявлением и применением я не нашел корреляции %(
|
|
|
|
|
Jul 31 2015, 07:51
|
Гуру
     
Группа: Свой
Сообщений: 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}; Сколько будет выделено памяти? Скорее всего будет выделено по максимуму.
|
|
|
|
|
Jul 31 2015, 08:22
|
Гуру
     
Группа: Свой
Сообщений: 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 * решать. делать указатель воид, и по какому то признаку правильно к нему обращаться,а юнионы тут излишни ИМХО
|
|
|
|
|
Jul 31 2015, 10:28
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
Jul 31 2015, 10:45
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Golikov A. @ Jul 31 2015, 14:22)  как я понимаю все метания автора не выделить памяти больше чем надо, А то так все можно было лонгами покрыть и всех делов.... Да, именно так. Имеется программа, в которой множество массивов структур, и массивов массивов структур. Многие члены этих структур - счётчики количества неких событий (разного типа событий, для каждого определён максимальный диапазон счётчика). Другие члены этих структур - индексы, указывающие на элементы других массивов (перекрёстные ссылки между массивами). Также есть ещё связные списки, в членах которых тоже есть индексы для перекрёстного связывания. Заранее точно не известны максимальные размерности для каждого из этих счётчиков (и соответственно индексов). Есть ещё кольцевые буфера, сложные, с многопозиционными указателями записи, позиции обработчика1 кольца, ..., позиции обработчикаN кольца, чтения (указатели - тоже индексы, не указатели на память (для экономии размера)). Макс. кол-во элементов кольцевых очередей тоже задаётся дефайнами. И тоже потом может быть изменено. Соответственно - размер этих массивов и кольцевых буферов будет сильно зависеть от размерности типов, хранящих эти счётчики и индексы. И если потом вдруг потребуется поменять пару дефайнов, неохота лазить по всему коду и править. Да в принципе подобные типы данных встречаются частенько и в других программах, только не в таком количестве  И мне всегда хотелось придумать какой-то способ объявления. Пока вижу два варианта решения: 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. С типом, созданным посредством препроцессора, ассемблерный код выглядит гораздо красивее
|
|
|
|
|
Jul 31 2015, 11:05
|
Гуру
     
Группа: Свой
Сообщений: 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) );
|
|
|
|
|
Jul 31 2015, 11:17
|
Гуру
     
Группа: Свой
Сообщений: 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 пересчитывалось компилятором по заданному выражению и не мной вручную. Так же хотелось бы делать и для типов: я указываю его максимальный размер, а компилятор по этому размеру считает в переменной какого размера его выгоднее всего хранить.
|
|
|
|
|
Jul 31 2015, 11:45
|
Гуру
     
Группа: Свой
Сообщений: 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;
|
|
|
|
|
Aug 1 2015, 07:09
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
тогда ну это не очень понятно  , макроса то никакого нет, в этом смысл.... более подробно вам надо чтобы вы в 1 месте сменили MAX_EVENTS, а у вас все счетчики для его подсчета сами бы поменяли размерность. делается через промежуточный дефайн объявляете базовый дефайн #define MAX_EVENTS 54 от этого дефайна объявляете тип #if MAX_EVENTS .... #define MyType int8_t #else #define MyType int16_t и так далее... а уже от этого типа объявляете счетчики MyType Counter; в результате, сменив MAX_EVENTS у вас автоматом смениться тип, то есть будет все как вы хотите, смена в одном месте, поменялись все типы... эти подмены работают автоматически, вам их надо написать один раз, и запаковать в файл. Громоздко на самом деле не будет, там всего то 4 строчки по числу типов, просто надо сравнивать в нужном направлении, чтобы проверять одну границу. Ну в общем делайте как хотите  это реально решение и методами языка С
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|