Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Гибкий размер типа данных
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
jcxz
Не знаю в какой раздел форума написать, пишу сюда.
Хочется иметь тип данных гибкого размера, с размером, зависящим от значения некоего 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.
scifi
Цитата(jcxz @ Jul 29 2015, 08:33) *
чтобы можно было объявить переменную:
static flexType(u, 54) x;

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

Блин...
Код
#define MAX_EVENTS 54  //кол-во обрабатываемых событий
static flexType(u, MAX_EVENTS) x;
Так понятнее?
Чтобы потом, если вдруг потребуется изменить кол-во обрабатываемых событий, не потребовалось лазить по всему коду и менять размер всех переменных, размерность которых зависит от MAX_EVENTS.
И конечно имеются массивы типа flexType(u, MAX_EVENTS)
scifi
Цитата(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). Ничего страшного, руки не отвалятся.
jcxz
Цитата(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
AHTOXA
На c++ это делается просто. Вот ссылка по теме. (Там и на си есть решение, но только для типов не меньше int).
dxp
На плюсах можно копать в сторону templates и traits. Несколько лет назад подобное (подобное - в смысле, генерация типа в зависимости от условий) делал для оптимизации типа, передаваемого в функцию. Там если размер передаваемого объекта был меньше 4 байт, то передавался обычный тип (а объект - по значению), а если больше - то константная ссылка. Подробностей не помню. Но, в общем, traits как раз для подобных целей и предназначены.
jcxz
Цитата(dxp @ Jul 29 2015, 19:59) *
На плюсах можно копать в сторону templates и traits.

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

SasaVitebsk
При написании меню мне понадобилось статически менять тип переменной. Для инициализации.
Задача не совсем Ваша, но может посмотрите и что-то заимствуете ...
Объявление:
Код
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;
Golikov A.
А что кстати дает такой тип кроме путаницы и зоопарка дефайнов?

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


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


и

Код
typedef int8_t MY_TYPE;
static MY_TYPE x;


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

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


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




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

чет между объявлением и применением я не нашел корреляции %(
SasaVitebsk
Цитата(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};

Сколько будет выделено памяти?
Скорее всего будет выделено по максимуму.
Golikov A.
понятно, ну тогда можно и так

char AbstractData[8];

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

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


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

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

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

biggrin.gif
Сергей Борщ
Цитата(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 бита?
jcxz
Цитата(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
Golikov A.
Цитата
Тяжелое ассемблерное детство...

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) );
jcxz
Цитата(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 пересчитывалось компилятором по заданному выражению и не мной вручную.
Так же хотелось бы делать и для типов: я указываю его максимальный размер, а компилятор по этому размеру считает в переменной какого размера его выгоднее всего хранить.
Golikov A.
а... ну теперь понятно

ну тогда вам надо просто написать 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;
scifi
Цитата(Golikov A. @ Jul 31 2015, 14:45) *
и через #if ....#else от значения MAX_EVENTS
определяете typdef или #define если тот закривляется

Семён Семёныч! Почему-то мне это в голову не пришло. Респект a14.gif
jcxz
Цитата(Golikov A. @ Jul 31 2015, 17:45) *
и через #if ....#else от значения MAX_EVENTS
определяете typdef или #define если тот закривляется

Ну это понятно. Я о таком ещё в 16-м сообщении писал (вариант 1).
Вот если-бы си позволял конструкцию #if/#endif в макрос запихнуть! sad.gif
Golikov A.
тогда ну это не очень понятно 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 это реально решение и методами языка С
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.