Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Таблица переходов вместо Switch
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
AndreyS
Добрый день.

Если я не первый раз поднимаю эту тему, то заранее извиняюсь (я не смог придумать поисковику удобоваримое слово для поиска. На void* или таблица вываливается куча страниц).

Хочу заменить конструкцию Switch case на таблицу переходов и все это на языке Си.

Как вызвать процедуру по адресу я знаю, вопрос как бы мне заполнить таблицу с 256 значениями.
На асме я это реализовывал так:
Сначала создавал массив адресов на обработчик default, а затем в другом инклюднике прописывал (переопределял по вычисленным адресам определенные обработчики) Вот так:
Код
COM_SN_WRITE     EQU    42h                    ; Команда записи SN
    ORG TABEL_COM+(COM_SN_WRITE*2)              ; тут умножаю на 2, так как храню адрес из 2-х байт, в Keil Си он 3-х байтовый
    DW SEND_COM_SN_WRITE                               ; Ну а тут собственно сам обработчик (процедура). Тут ее адрес


SEND_COM_SN_WRITE:
       RET


TABEL_COM это адрес таблицы
COM_SN_WRITE номер команды
SEND_COM_SN_WRITE адрес обработчика

Как бы мне теперь так же сделать на Си в Keil?

PS Работу switch в Keil я разобрал, он работает с таблицей не так как мне нужно (она все равно перебором просматривает всю свою таблицу).
aaarrr
Определите массив указателей на функции, например, массив из 10 указателей на функции вида int func(int, int) будет выглядеть так:
Код
int (*zzz[10])(int, int);
Dog Pawlowa
Код
typedef void (*VECTORS)();

const VECTORS function[stQty] =
{
  f0,  f1,  f2,  f3,  f4,  f5,  f6,  f7,  f8,  f9,
  f10, f11, f12, f13, f14, f15, f16, f17, f18, f19,
  f20, f21, f22, f23, f24, f25, f26, f27, f28, f29,
  f30, f31, f32, f33, f34, f35, f36, f37, f38, f39,
  f40, f41, f42, f43, f44, f45, f46, f47, f48, f49,
  f50, f51, f52, f53, f54, f55, f56, f57, f58, f59,
  f60, f61, f62, f63, f64, f65, f66, f67, f68, f69,
  f70, f71, f72, f73, f74, f75, f76, f77, f78, f79,
  f80, f81, f82, f83, f84, f85, f86, f87, f88, f89,
  f90, f91, f92, f93, f94, f95, f96, f97, f98, f99,
  f100,f101,f102,f103
   };


Цитата(AndreyS @ Mar 23 2010, 19:28) *
...PS Работу switch в Keil я разобрал, он работает с таблицей не так как мне нужно (она все равно перебором просматривает всю свою таблицу).

Оптимизация включена?
baralgin
Цитата(Dog Pawlowa @ Mar 23 2010, 18:30) *
Оптимизация включена?

По идее компилятор такой самодеятельностью заниматься не должен. Ведь для этого понадобится дополнительная статическая память. А так конечно, нужно разобраться что такое указатели на функции, и дальше наиболее оптимальные решения будут сами проситься.
Dog Pawlowa
Цитата(baralgin @ Mar 23 2010, 20:08) *
По идее компилятор ....
Ведь для этого понадобится дополнительная статическая память.

ИАР, к примеру, занимается.
Зачем статическая память? Все можно сделать в программной памяти.
zltigo
Цитата(baralgin @ Mar 23 2010, 20:08) *
По идее компилятор такой самодеятельностью заниматься не должен.

Вот, как мало Вы знаете о приличных компиляторах sad.gif, хотя я вообще-то давно не видел компиляторов, которые таким не занимаются (причем не огульно, а действительно выбирая компромис), если, конечно не гробить им явно оптимизацию.
AndreyS
Цитата(Dog Pawlowa @ Mar 23 2010, 19:30) *
[code]typedef void (*VECTORS)();

const VECTORS function[stQty]={указатели}

Оптимизация включена?

спасибо большое за ответ. Меня интересовало как в таблицу, в произвольном порядке прописать адрес функции. Вот например таблица изначально заполнена адресом на один обработчик заглушку. Теперь я создал например обработчик 10-го позиционного номера таблицы. И хочу что бы компилятор в таблице заменил адрес 10 ячейки этой таблицы. На асме я это регулировал оргом, тут с _at_ такая штука не проходит. а нужно мне это для дополнения обработчиков. Ну всего 256 комбинаций (у меня байт проверяется), из них 200 определено. И вот хочу еще 2 определить, что бы не искать позицию в таблице, я хотел что бы компилятор (по моему указанию) высчитал позицию адреса в таблице и туда этот адрес положил. А как вызвать функию по адресу из этой таблицы я знаю, спасибо.

Цитата(Dog Pawlowa @ Mar 23 2010, 19:30) *
Оптимизация включена?

оптимхизация включена (какой уровень сейчас не скажу да это и не важно). У кейла в факе на сайте по этому поводу сказано что начиная 7 версии свитчь сам сворачивается в таблицу в зависимости отусловий (от кода). Я стал анализировать. Он таблицу создает сразу от 6 кейсов, если кейсы стоят последовательно (т.е. Сравниваемое значение в каждом кейсе отличается от предыдущего на 1). Если их перетасовать, то таблица появляетя уже если кейсов более 10. Но это все фигня. Я зашел в их функию ccall за которой в коде лежит таблица, и посмотрел что она делает. Оказалось что 3-х байтовая таблица это не просто адрес. А 2 байта адрес и значение кейса. И фукция производит в цикле поиск значенния первого байта адреса равного 0. Это означает для нее что вся таблица перебрана и совпадения нет. И тут находится адрес обработчика дефаулт значения. Если первый байт в таблице не равен 0, то тогда сравнивается 3-й байт в таблице (в нем лежит кейс значение). Если он совпал с входящим числом (switch), то выполняется переход на адрес (1 и 2 ячейки таблицы). Иначе ищет дальше. Нахрена такая таблица я не понял. Компактно - да, быстро - нет.
baralgin
Dog Pawlowa, zltigo, Спасибо, буду знать, а то больших свитчей как-то побаивался.

AndreyS, Что-то не совсем понятно. У вас есть две сотни обработчиков? Изменить в таблице(массив указателей) адрес функции не сложно. Хотя если вы уже инициализировали этот массив, то в чём вообще проблема?
mas_func[10] = &new_handler;
// где new_handler ваша функция(естественно такого же типа как и элементы массива).
Dog Pawlowa
Цитата(AndreyS @ Mar 23 2010, 22:51) *
Меня интересовало как в таблицу, в произвольном порядке прописать адрес функции.

Ну пишете в произвольком порядке и все, вот как я: wink.gif

Код
/ basic functions:
STATE ( Restart            ,    "Restart"          ,  "Neustart"         ,    0        )
STATE ( SelfTest           ,    "Self-test"        ,  "Selbsttest"       , CLR+STN    )
STATE ( Waiting            ,    "Ready"            ,  "Bereit"           ,    0        )
STATE ( AirDeleting        ,    "Air out "         ,  "Entl"uuml"ften"   , CLR+STN    )


Из этой таблицы генерируются и коды, и имена функций, и данные для заполнения массивов.
Только не занимайтесь "ручной" линковкой "а-ля ассемблер".

Цитата(baralgin @ Mar 23 2010, 23:18) *
Изменить в таблице(массив указателей) адрес функции не сложно. ..
mas_func[10] = &new_handler;

Если таким образом программируется машина состояний, но таблицу незачем располагать в памяти данных.
Я понял, что задача автора сделать удобной инициализацию массива. Или?
AHTOXA
Цитата(AndreyS @ Mar 24 2010, 00:10) *
Меня интересовало как в таблицу, в произвольном порядке прописать адрес функции. Вот например таблица изначально заполнена адресом на один обработчик заглушку. Теперь я создал например обработчик 10-го позиционного номера таблицы. И хочу что бы компилятор в таблице заменил адрес 10 ячейки этой таблицы.


Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так:
Код
// объявления всех нужных обработчиков
#define WEAK __attribute__ ((weak))
void WEAK f00(void);
void WEAK f01(void);
...
void WEAK f255(void);

//таблица :
void (* const Handlers[])(void) =
{
    f00,
    f01,
...
    f255
}

// и заглушки по умолчанию:
#pragma weak f00 = Default_Handler
#pragma weak f01 = Default_Handler
...
#pragma weak f255 = Default_Handler


Теперь для объявления своего обработчика №25 просто пишем в любом c-файле
Код
void f25()
{
    //
}

и эта функция попадает в нужное место.

Но в кейле для этого приходится залезать в ассемблерsmile.gif

Код
        MACRO
        def_int_vector  $HandlerName
$HandlerName  PROC
              EXPORT  $HandlerName  [WEAK]
              B  .
              ENDP
        MEND
AndreyS
Цитата(Dog Pawlowa @ Mar 23 2010, 22:25) *
Ну пишете в произвольком порядке и все, вот как я: wink.gif

Из этой таблицы генерируются и коды, и имена функций, и данные для заполнения массивов.
Только не занимайтесь "ручной" линковкой "а-ля ассемблер".

Я понял, что задача автора сделать удобной инициализацию массива. Или?

ууу. С вашим кодом я поплыл, что такое state я не знаю :-(

я был не полностью откровенен. Keil в данном случае 51 ядро, массив конечно лежит в областе code.

Вы совершенно правы, что задача стоит в удобстве инициализации массива на стадии компиляции(или линковки это не важно, главное что не в процессе выполнения программы).

На асме то я как раз и не хотел делать (это у меня в старых проектах моих, написаных мной на асме). Пишу все время на си, посто потом смотрю что на асме получилось и оптимзирую запись на си, так что бы на асме красиво было (быстро). А со свичем не стал париться, но тут у меня кейс разросся и решил переделать свитчь на таблицу (таблица будет со временем переопределяться, дописываться будут новые обработчики). Вот и хотел написать макрос, который бы сам по числу проверки заносил адрес обработчика в общую таблицу в требуюмую ячейку с замещением в ней адреса дефолтового обработчика. Вот вопрос как?

Цитата(AHTOXA @ Mar 23 2010, 22:38) *
Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так:
Код
код проглотил

и эта функция попадает в нужное место.


во. Это то что мне нужно. Попробую покрутить в keilе 51.
Dog Pawlowa
Цитата(AndreyS @ Mar 23 2010, 23:59) *
С вашим кодом я поплыл, что такое state я не знаю :-(

Это просто мой макрос, определение которого меняется в программе несколько раз.
Не "грязный хак", но любители строгого С обычно стоят в сторонке. smile.gif
AndreyS
Цитата(Dog Pawlowa @ Mar 23 2010, 23:07) *
Это просто мой макрос, определение которого меняется в программе несколько раз.
Не "грязный хак", но любители строгого С обычно стоят в сторонке. smile.gif


ага. Значит читать мне надо про макросы?

Цитата(AHTOXA @ Mar 23 2010, 22:38) *
Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так:
...
и эта функция попадает в нужное место.

а как бы к этому математику подтянут?. Пример. Имеем команду ENTER, она имеет числовое значение равное 10. Получается что ее обработчик f10. А теперь положим сменилось ее числовое значение на другое. Получается нужно не забыть переименовать обработчик.
Как бы привязать заполнение таблицы к номеру команды? И исходя из ее значения в соответствующую ячейку таблицы попадал адрес обработчика. Сменил я номер команды и адрес автоммтом лег в новую ячейку, а в старую опять вернулся адрес дефаулт обработчика.
Dog Pawlowa
Цитата(AndreyS @ Mar 24 2010, 00:14) *
ага. Значит читать мне надо про макросы?

Желательно, только сразу не лезьте в дебри.
Альтернатива предложению от АНТОХА:
В простейшем виде пишете
#define f25 /*empty_function*/my_function_for_code_25

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


Цитата(AndreyS @ Mar 24 2010, 00:41) *
Как бы привязать заполнение таблицы к номеру команды?

Ладно, покажу, как это делается у меня, может другие более простой способ подскажут.

Код
enum {
#define STATE( name, engl, germ, prop) st##name,
#include STATES_HEADER
#undef STATE
stQty
};


const VECTORS function[stQty] = {
#define STATE( name, engl, germ, prop) f##name ,
#include STATES_HEADER
#undef STATE
};
AHTOXA
Цитата(AndreyS @ Mar 24 2010, 01:41) *
а как бы к этому математику подтянут?. Пример. Имеем команду ENTER, она имеет числовое значение равное 10. Получается что ее обработчик f10. А теперь положим сменилось ее числовое значение на другое. Получается нужно не забыть переименовать обработчик.
Как бы привязать заполнение таблицы к номеру команды?


Ну это уже мелочиsmile.gif

Код
#define DECLARE_COMMAND_HANDLER2(cmd_number) void f##cmd_number(void)
#define DECLARE_COMMAND_HANDLER(cmd_number) DECLARE_COMMAND_HANDLER2(cmd_number)

#define CMD_ENTER  10

DECLARE_COMMAND_HANDLER(CMD_ENTER)
{
  for (;;);
}


Код
DECLARE_COMMAND_HANDLER(CMD_ENTER)

развернётся в
Код
void f10(void)


При смене CMD_ENTER на 20 - станет
Код
void f20(void)
.


Цитата(Dog Pawlowa @ Mar 24 2010, 01:55) *
В простейшем виде пишете
#define f25 /*empty_function*/my_function_for_code_25


Это отличный вариант, пока все обработчики хранятся в одном файле. Но иногда (часто) нужно, чтоб модуль при подключении автоматически добавлял свой элемент в таблицу. В случае фиксированной таблицы (с известным заранее числом элементов, типа таблицы векторов прерываний) - можно применить вариант, который я привёл. А вот для произвольной таблицы - я так ничего путного и не придумал.
А как было бы здорово, если бы модуль (.c файл) мог при его добавлении в проект, скажем, автоматически добавить несколько своих терминальных команд, пару переменных в конфиг, етцsmile.gif
Придумалось только для плюсов, через смесь шаблонов и макросов. Да и то не всё там гладко, накладные расходы по ОЗУ (не всё во флеше), и работоспособность зависит от порядка вызова глобальных конструкторов (не определён стандартом, емнимс).
Dog Pawlowa
Цитата(AHTOXA @ Mar 24 2010, 01:54) *
Это отличный вариант, пока все обработчики хранятся в одном файле.

Ну почему, они отлично подключаются через заголовочные файлы.
Но автоматически не добавляются, ага.
Может это и хорошо? wink.gif
sonycman
Цитата(AndreyS @ Mar 23 2010, 23:10) *
оптимхизация включена (какой уровень сейчас не скажу да это и не важно).
...
Нахрена такая таблица я не понял. Компактно - да, быстро - нет.

Как это не важно?
У Вас какой режим оптимизации включен, по размеру или по времени?
А если по размеру? Вот он вам такой код и генерит.
Поставте О3, и включите опцию оптимизации по времени.
Возможно, и не понадобится вся эта канитель, что тут развели smile.gif
AndreyS
Цитата(sonycman @ Mar 27 2010, 09:48) *
Как это не важно?
У Вас какой режим оптимизации включен, по размеру или по времени?
А если по размеру? Вот он вам такой код и генерит.
Поставте О3, и включите опцию оптимизации по времени.
Возможно, и не понадобится вся эта канитель, что тут развели smile.gif



Добрый день.

Тип оптимизации по времени с повторным использованием одинакового куска кода (сворачиват повторения в процедуры).

А канитель эта по любому нужна wink.gif
defunct
Цитата(AndreyS @ Mar 23 2010, 22:10) *
Теперь я создал например обработчик 10-го позиционного номера таблицы. И хочу что бы компилятор в таблице заменил адрес 10 ячейки этой таблицы. На асме я это регулировал оргом, тут с _at_ такая штука не проходит. а нужно мне это для дополнения обработчиков. Ну всего 256 комбинаций (у меня байт проверяется), из них 200 определено. И вот хочу еще 2 определить, что бы не искать позицию в таблице, я хотел что бы компилятор (по моему указанию) высчитал позицию адреса в таблице и туда этот адрес положил. А как вызвать функию по адресу из этой таблицы я знаю, спасибо.


А что мешает просто красиво задать массив, - с одной функцией в одной строке и коментарием в конце строки, который будет указывать номер текущей позиции:

Код
void CommonHandler()
{
}

void GetTimeHander()
{
  ...
}

void (*Handlers[])(void) =
{
    CommonHandler,      // 0
    CommonHandler,      // 1
    CommonHandler,      // 2
    CommonHandler,      // 3
    GetTimeHandler,     // 4
    CommonHandler,      // 5
...
    CommonHandler,      // 55
    CommonHandler,      // 56
    CommonHandler,      // 57
...
    CommonHandler,      // 254
    CommonHandler       // 255
};

И вручную следить за чистотой этого массива. Найти место для пары новых обработчиков когда все в чистоте не составит никакой сложности. Да и обратная операция - читать этот массив человеку который будет поддерживать код после вас будет легко, - ему не придется бегать к каждой функции и смотреть в какую позицию она загружена.
Dog Pawlowa
Цитата(defunct @ Apr 5 2010, 02:56) *
А что мешает просто красиво задать массив, - с одной функцией в одной строке и коментарием в конце строки, который будет указывать номер текущей позиции:
...И вручную следить за чистотой этого массива.

Я делал так раньше, неудобно - слишком много переименовывать.
Как раз на днях заказчик попросил поменять последовательность функций в менюшке. Одно дело перетасовать имена функций, другое дело отслеживать физические индексы. Не говоря уже о том, чтобы добавить раздел в меню.
RobFPGA
Приветствую!


Цитата(AHTOXA @ Mar 24 2010, 00:54) *
Это отличный вариант, пока все обработчики хранятся в одном файле. Но иногда (часто) нужно, чтоб модуль при подключении автоматически добавлял свой элемент в таблицу. В случае фиксированной таблицы (с известным заранее числом элементов, типа таблицы векторов прерываний) - можно применить вариант, который я привёл. А вот для произвольной таблицы - я так ничего путного и не придумал.
А как было бы здорово, если бы модуль (.c файл) мог при его добавлении в проект, скажем, автоматически добавить несколько своих терминальных команд, пару переменных в конфиг, етцsmile.gif
Придумалось только для плюсов, через смесь шаблонов и макросов. Да и то не всё там гладко, накладные расходы по ОЗУ (не всё во флеше), и работоспособность зависит от порядка вызова глобальных конструкторов (не определён стандартом, емнимс).


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

Успехов! Rob/
defunct
Цитата(Dog Pawlowa @ Apr 5 2010, 13:24) *
Я делал так раньше, неудобно - слишком много переименовывать.
Как раз на днях заказчик попросил поменять последовательность функций в менюшке. Одно дело перетасовать имена функций, другое дело отслеживать физические индексы. Не говоря уже о том, чтобы добавить раздел в меню.

Если смотреть в контексте использования для какого-то протокола из-вне (модбас / мэк101 / 104 и т.п. много всяких у кого 256 функций), где функции (большинство их) жестко определены стандартом. То существенно менять ничего не придется, никто функции протокола менять не будет. Функции могут только неспешно добавляться как и отметил автор ветки.

А для меню думаю куда удобнее и нагляднее хранить описатель пункта меню, который включает что-то типа
- ссылку на верхнее меню (parent)
- ссылку на субменю (если есть)
- имя пункта (то что выводится на экран)
- параметры шрифта
- ссылку на функцию-аутентификатор (не все же можно выполнять сразу, кое-что может требовать особого подтверждения)
- ну и ссылку на действие. (специально поставил последним)

вместо таблицы голых функций.
dxp
Цитата(AHTOXA @ Mar 24 2010, 04:54) *
работоспособность зависит от порядка вызова глобальных конструкторов (не определён стандартом, емнимс).

С этим можно побороться с помощью паттерна Singleton.

Код
//---------------------------------------------------------------------------
template<typename T> class singleton
{
public:
    static T& Instance() { static T instance; return instance; }
};
//---------------------------------------------------------------------------


Использование:

Код
class TSlon { ... };


TSlon& slon = singleton<TSlon>::Instance();

// далее идет использование слона (синтаксически как обычный объект):
// при создании ссылки внутри singleton<TSlon> будет
// статически создан и проинициализирован объект внутри Instance(), поэтому
// ссылку можно безопасно использовать

Есть небольшие накладняки на создание статического объекта, но они небольшие - соизмеримы с вызовом конструктора. Работает этот прием очень хорошо.
Dog Pawlowa
Цитата(defunct @ Apr 6 2010, 04:40) *
А для меню думаю куда удобнее и нагляднее ...

А я не исключаю включение в массив дополнительно к указателям на функции вспомогательной информации. Но для меня реализация всего интерфейса пользователя как единого автомата состояний получается гораздо прозрачнее и удобнее. Законченный фрагмент исходника должен поместиться на экране. Меньше ссылок, больше прямых действий. От "глобальной" индесации массива функций [state][event] я сделал шаг назад, к function[state].
Впрочем, я уже о своем, наболевшем - не перебдеть в структурировании smile.gif
AHTOXA
Цитата(dxp @ Apr 6 2010, 09:05) *
Код
// при создании ссылки внутри singleton<TSlon> будет
// статически создан и проинициализирован объект внутри Instance(), поэтому
// ссылку можно безопасно использовать


А чем нам поможет замена глобального объекта на глобальный статический? В плане руления порядком вызова конструкторов? То есть, чем это отличается от простого объявления
Код
TSlon slon;
?
Имхо, без динамического создания объектов singleton совершенно бесполезен.
dxp
Цитата(AHTOXA @ Apr 6 2010, 14:45) *
А чем нам поможет замена глобального объекта на глобальный статический? В плане руления порядком вызова конструкторов? То есть, чем это отличается от простого объявления

Вот зрасьте! В этом же вся фишка синглтона. Когда какой-то объект нуждается в объекте TSlon, то он создает (использует) ссылку, возвращаемую функцией Instance() шаблона singleton. А ссылка эта ссылается на статический объект, создаваемый внутри фукнции-члена Instance(). Таким образом, получается следующая схема: при первом вызове Instance() будет создан и проинициализирован статический объект требуемого типа (в нашем примере TSlon), все последующие вызовы будут возвращать ссылку на уже существующий объект. И не важно, кто первый дернул Instance(), думать об этом не надо. В этом и избавление от зависимости от порядка вызова конструкторов.

Цитата(AHTOXA @ Apr 6 2010, 14:45) *
Код
TSlon slon;
?
Имхо, без динамического создания объектов singleton совершенно бесполезен.

Еще как полезен: объект создается как статический внутри статической же функции-члена Instance(). И живет там все время, пока программа работает. Т.е. по сути как обынчый глобальный объект, но спрятанный внутри функции и предоставляющий доступ по ссылке (что синтаксически ровно как доступ к обычному объекту). Нету тут ни работы с кучей, ни выделения памяти на рантайме. Выделение памяти и инициализация производятся комилятором на основе правил языка в части статических объектов внутри функций (которые есть даже в голом С).
AHTOXA
Цитата(dxp @ Apr 6 2010, 17:14) *
Вот зрасьте! В этом же вся фишка синглтона.


Упс. Я был уверен, что разница между статическим и глобальным объектом только в области видимости.
А ты хочешь сказать, что для статических объектов конструкторы вызываются не в списке глобальных конструкторов, а при первом вызове функции, в которой он содержится? Если так, то это супер! Надо проверитьsmile.gif
dxp
Цитата(AHTOXA @ Apr 6 2010, 19:15) *
А ты хочешь сказать, что для статических объектов конструкторы вызываются не в списке глобальных конструкторов, а при первом вызове функции, в которой он содержится? Если так, то это супер! Надо проверитьsmile.gif

Статический объект должен быть гарантировано проинициализирован до его использования. А где это делается - отдано на откуп реализации. Например:
Код
void f()
{
    int a = 10;
    ... // код функции
}

Реализация может быть разной. Самое простое - сунуть а в секцию, где помещаются глобальные переменные, и проинициализировать вместе с ними (IAR, помнится, так и делает) - разница тут будет только в области видимости. А можно тут вставить код, проверяющий первый вход в функцию, и вызывать инициализатор (это тот самый оверхед, про который я говорил, и который незначителен по сравнению с вызовом конструктора). Как делается реально в том или ином случае - это в ведении реализации (в случае с вызовом конструктора объекта пользовательского типа скорее всего делается по второму варианту). Главное, что поведение четко стандартизовано и это можно безопасно использовать.
AHTOXA
Цитата(dxp @ Apr 6 2010, 18:56) *
Главное, что поведение четко стандартизовано и это можно безопасно использовать.


Попробовал, получилось. Но далеко не сразу. Простая замена глобального объекта
Код
uart1_t uart1;

на ссылку:
Код
uart1_t& uart1 = singleton<uart1_t>::Instance();

Не прокатила. Потому что теперь вместо глобального объекта uart1 у нас получилась глобальная же ссылкаsmile.gif Порядок инициализации которой (на этот раз при помощи хитрого синглетона!) по прежнему не определён.
Пришлось везде, где используется uart1, заводить локальные переменные-ссылки
Код
uart1_t& localuart = singleton<uart1_t>::Instance();

и использовать их. Вот тогда всё заработало должным образомsmile.gif
Что касаемо накладных расходов, то они достаточно приличные:
Код
*с глобальным uart:
   text    data     bss     dec     hex filename
   4372       8    5792   10172    27bc ./exe/stm32-H103.elf
*с singleton<uart1_t>:
   text    data     bss     dec     hex filename
   4500       8    5800   10308    2844 ./exe/stm32-H103.elf

(Всё это gcc на stm32)

Я думаю, что для некоторых применений это хорошее решение, по крайней мере я намерен его применять.
(Или всё же new? smile.gif )
dxp
Цитата(AHTOXA @ Apr 7 2010, 01:12) *
Пришлось везде, где используется uart1, заводить локальные переменные-ссылки
Код
uart1_t& localuart = singleton<uart1_t>::Instance();

и использовать их. Вот тогда всё заработало должным образомsmile.gif

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

Цитата(AHTOXA @ Apr 7 2010, 01:12) *
Я думаю, что для некоторых применений это хорошее решение, по крайней мере я намерен его применять.
(Или всё же new? smile.gif )

Для new надо свой быстрый и не подверженный фрагментации менеджер памяти сделать. Если только для вышеописанных целей, то это будет еще больше накладных. А вообще такой мененжер памяти местами очень полезен. Надо будет сделать, как руки дойдут.
AHTOXA
Цитата(dxp @ Apr 7 2010, 10:06) *
Для new надо свой быстрый и не подверженный фрагментации менеджер памяти сделать. Если только для вышеописанных целей, то это будет еще больше накладных. А вообще такой мененжер памяти местами очень полезен.


Если new будет использоваться только как замена глобальным объектам, то фрагментация ему не грозит, ибо будет только new, без delete. Но если
Цитата
Надо будет сделать, как руки дойдут.

, то я только заsmile.gif
ЗЫ. Пока прикрутил bget, вроде работает.
dxp
Цитата(AHTOXA @ Apr 7 2010, 11:22) *
Если new будет использоваться только как замена глобальным объектам, то фрагментация ему не грозит, ибо будет только new, без delete.

А тогда смысла нет в динамическом управлении памятью. Смысл ведь в том, чтобы юзать одну и ту же память под разные цели в разные моменты времени. Как стек, только со временем жизни объектов, определяемым пользователем. А если без delete, то тот вариант во статиками внутри получается функционально почти такой же, а по накладным лучше.
AHTOXA
Цитата(dxp @ Apr 7 2010, 12:39) *
А тогда смысла нет в динамическом управлении памятью. Смысл ведь в том, чтобы юзать одну и ту же память под разные цели в разные моменты времени.

Смысл в том, что объекты создаются в нужном мне порядке, я управляю этим.
Насчёт увеличения накладных - это вряд ли. ну разве что при создании объекта. При работе - простейшая проверка на null и возврат.
Естественно, если будут нормальные new/delete, то лучше использовать их.
dxp
Цитата(AHTOXA @ Apr 7 2010, 15:06) *
Смысл в том, что объекты создаются в нужном мне порядке, я управляю этим.

От неопределенности порядка вызовов конструкторов глобальных объектов ты не избавишься, размещая объект в свободной памяти вместо того, чтобы завести его статиком внутри функции. Это имелось в виду.
AHTOXA
А, это конечно. Просто наличие new позволяет даже без всяких синглетонов делать
Код
if (!uart) uart = new uart_t;

, а в случае глобальных объектов я этого лишён.
Статики (спасибо за идею) позволяют обойти это ограничение.
Консенсус? smile.gif
dxp
Цитата(AHTOXA @ Apr 7 2010, 20:59) *
Код
if (!uart) uart = new uart_t;

Да, это удобная штука (а местами незаменимая - сейчас приходится с GUI колупаться, очень не хватает простого, легкого и безопасного менеджера памяти. И для STL'ных вещей оно тоже очень нужно, там, правда, придется аллокаторы переписать). Надо будет поставить приоритетной в очередь факультативных задач.

Цитата(AHTOXA @ Apr 7 2010, 20:59) *
Консенсус? smile.gif

Адназначна. cheers.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.