|
Таблица переходов вместо Switch, Keil Си |
|
|
|
Mar 23 2010, 15:28
|

Местный
  
Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276

|
Добрый день. Если я не первый раз поднимаю эту тему, то заранее извиняюсь (я не смог придумать поисковику удобоваримое слово для поиска. На 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 я разобрал, он работает с таблицей не так как мне нужно (она все равно перебором просматривает всю свою таблицу).
--------------------
Удачи.
|
|
|
|
|
 |
Ответов
|
Mar 23 2010, 16:30
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Код 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 я разобрал, он работает с таблицей не так как мне нужно (она все равно перебором просматривает всю свою таблицу). Оптимизация включена?
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 23 2010, 19:10
|

Местный
  
Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276

|
Цитата(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 ячейки таблицы). Иначе ищет дальше. Нахрена такая таблица я не понял. Компактно - да, быстро - нет.
--------------------
Удачи.
|
|
|
|
|
Mar 23 2010, 19:25
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(AndreyS @ Mar 23 2010, 22:51)  Меня интересовало как в таблицу, в произвольном порядке прописать адрес функции. Ну пишете в произвольком порядке и все, вот как я:  Код / 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; Если таким образом программируется машина состояний, но таблицу незачем располагать в памяти данных. Я понял, что задача автора сделать удобной инициализацию массива. Или?
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 23 2010, 19:59
|

Местный
  
Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276

|
Цитата(Dog Pawlowa @ Mar 23 2010, 22:25)  Ну пишете в произвольком порядке и все, вот как я:  Из этой таблицы генерируются и коды, и имена функций, и данные для заполнения массивов. Только не занимайтесь "ручной" линковкой "а-ля ассемблер". Я понял, что задача автора сделать удобной инициализацию массива. Или? ууу. С вашим кодом я поплыл, что такое state я не знаю :-( я был не полностью откровенен. Keil в данном случае 51 ядро, массив конечно лежит в областе code. Вы совершенно правы, что задача стоит в удобстве инициализации массива на стадии компиляции(или линковки это не важно, главное что не в процессе выполнения программы). На асме то я как раз и не хотел делать (это у меня в старых проектах моих, написаных мной на асме). Пишу все время на си, посто потом смотрю что на асме получилось и оптимзирую запись на си, так что бы на асме красиво было (быстро). А со свичем не стал париться, но тут у меня кейс разросся и решил переделать свитчь на таблицу (таблица будет со временем переопределяться, дописываться будут новые обработчики). Вот и хотел написать макрос, который бы сам по числу проверки заносил адрес обработчика в общую таблицу в требуюмую ячейку с замещением в ней адреса дефолтового обработчика. Вот вопрос как? Цитата(AHTOXA @ Mar 23 2010, 22:38)  Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так: Код код проглотил и эта функция попадает в нужное место. во. Это то что мне нужно. Попробую покрутить в keilе 51.
--------------------
Удачи.
|
|
|
|
|
Mar 23 2010, 20:41
|

Местный
  
Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276

|
Цитата(Dog Pawlowa @ Mar 23 2010, 23:07)  Это просто мой макрос, определение которого меняется в программе несколько раз. Не "грязный хак", но любители строгого С обычно стоят в сторонке.  ага. Значит читать мне надо про макросы? Цитата(AHTOXA @ Mar 23 2010, 22:38)  Таким макаром сделаны обработчики прерываний для Cortex-M3 STM32. Для gcc это делается так: ... и эта функция попадает в нужное место. а как бы к этому математику подтянут?. Пример. Имеем команду ENTER, она имеет числовое значение равное 10. Получается что ее обработчик f10. А теперь положим сменилось ее числовое значение на другое. Получается нужно не забыть переименовать обработчик. Как бы привязать заполнение таблицы к номеру команды? И исходя из ее значения в соответствующую ячейку таблицы попадал адрес обработчика. Сменил я номер команды и адрес автоммтом лег в новую ячейку, а в старую опять вернулся адрес дефаулт обработчика.
Сообщение отредактировал rezident - Mar 24 2010, 01:39
Причина редактирования: Нарушение п.3.4 Правил форума.
--------------------
Удачи.
|
|
|
|
|
Mar 23 2010, 21:54
|

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

|
Цитата(AndreyS @ Mar 24 2010, 01:41)  а как бы к этому математику подтянут?. Пример. Имеем команду ENTER, она имеет числовое значение равное 10. Получается что ее обработчик f10. А теперь положим сменилось ее числовое значение на другое. Получается нужно не забыть переименовать обработчик. Как бы привязать заполнение таблицы к номеру команды? Ну это уже мелочи  Код #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 файл) мог при его добавлении в проект, скажем, автоматически добавить несколько своих терминальных команд, пару переменных в конфиг, етц  Придумалось только для плюсов, через смесь шаблонов и макросов. Да и то не всё там гладко, накладные расходы по ОЗУ (не всё во флеше), и работоспособность зависит от порядка вызова глобальных конструкторов (не определён стандартом, емнимс).
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Apr 6 2010, 03:05
|

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

|
Цитата(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(), поэтому // ссылку можно безопасно использовать Есть небольшие накладняки на создание статического объекта, но они небольшие - соизмеримы с вызовом конструктора. Работает этот прием очень хорошо.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Apr 6 2010, 07:45
|

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

|
Цитата(dxp @ Apr 6 2010, 09:05)  Код // при создании ссылки внутри singleton<TSlon> будет // статически создан и проинициализирован объект внутри Instance(), поэтому // ссылку можно безопасно использовать А чем нам поможет замена глобального объекта на глобальный статический? В плане руления порядком вызова конструкторов? То есть, чем это отличается от простого объявления Код TSlon slon; ? Имхо, без динамического создания объектов singleton совершенно бесполезен.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Apr 6 2010, 11:14
|

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

|
Цитата(AHTOXA @ Apr 6 2010, 14:45)  А чем нам поможет замена глобального объекта на глобальный статический? В плане руления порядком вызова конструкторов? То есть, чем это отличается от простого объявления Вот зрасьте! В этом же вся фишка синглтона. Когда какой-то объект нуждается в объекте TSlon, то он создает (использует) ссылку, возвращаемую функцией Instance() шаблона singleton. А ссылка эта ссылается на статический объект, создаваемый внутри фукнции-члена Instance(). Таким образом, получается следующая схема: при первом вызове Instance() будет создан и проинициализирован статический объект требуемого типа (в нашем примере TSlon), все последующие вызовы будут возвращать ссылку на уже существующий объект. И не важно, кто первый дернул Instance(), думать об этом не надо. В этом и избавление от зависимости от порядка вызова конструкторов. Цитата(AHTOXA @ Apr 6 2010, 14:45)  Код TSlon slon; ? Имхо, без динамического создания объектов singleton совершенно бесполезен. Еще как полезен: объект создается как статический внутри статической же функции-члена Instance(). И живет там все время, пока программа работает. Т.е. по сути как обынчый глобальный объект, но спрятанный внутри функции и предоставляющий доступ по ссылке (что синтаксически ровно как доступ к обычному объекту). Нету тут ни работы с кучей, ни выделения памяти на рантайме. Выделение памяти и инициализация производятся комилятором на основе правил языка в части статических объектов внутри функций (которые есть даже в голом С).
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Apr 6 2010, 12:56
|

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

|
Цитата(AHTOXA @ Apr 6 2010, 19:15)  А ты хочешь сказать, что для статических объектов конструкторы вызываются не в списке глобальных конструкторов, а при первом вызове функции, в которой он содержится? Если так, то это супер! Надо проверить  Статический объект должен быть гарантировано проинициализирован до его использования. А где это делается - отдано на откуп реализации. Например: Код void f() { int a = 10; ... // код функции } Реализация может быть разной. Самое простое - сунуть а в секцию, где помещаются глобальные переменные, и проинициализировать вместе с ними (IAR, помнится, так и делает) - разница тут будет только в области видимости. А можно тут вставить код, проверяющий первый вход в функцию, и вызывать инициализатор (это тот самый оверхед, про который я говорил, и который незначителен по сравнению с вызовом конструктора). Как делается реально в том или ином случае - это в ведении реализации (в случае с вызовом конструктора объекта пользовательского типа скорее всего делается по второму варианту). Главное, что поведение четко стандартизовано и это можно безопасно использовать.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Apr 6 2010, 17:57
|

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

|
Цитата(dxp @ Apr 6 2010, 18:56)  Главное, что поведение четко стандартизовано и это можно безопасно использовать. Попробовал, получилось. Но далеко не сразу. Простая замена глобального объекта Код uart1_t uart1; на ссылку: Код uart1_t& uart1 = singleton<uart1_t>::Instance(); Не прокатила. Потому что теперь вместо глобального объекта uart1 у нас получилась глобальная же ссылка  Порядок инициализации которой (на этот раз при помощи хитрого синглетона!) по прежнему не определён. Пришлось везде, где используется uart1, заводить локальные переменные-ссылки Код uart1_t& localuart = singleton<uart1_t>::Instance(); и использовать их. Вот тогда всё заработало должным образом  Что касаемо накладных расходов, то они достаточно приличные: Код *с глобальным 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?  )
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Apr 7 2010, 03:51
|

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

|
Цитата(AHTOXA @ Apr 7 2010, 01:12)  Пришлось везде, где используется uart1, заводить локальные переменные-ссылки Код uart1_t& localuart = singleton<uart1_t>::Instance(); и использовать их. Вот тогда всё заработало должным образом  Конечно, именно так и надо делать, в этом и смысл. Точнее, глобальную ссылку завести тоже не лишне, но ее использовать по ходу программы, а вот в конструкторах - да, только локальные ссылки. Цитата(AHTOXA @ Apr 7 2010, 01:12)  Я думаю, что для некоторых применений это хорошее решение, по крайней мере я намерен его применять. (Или всё же new?  ) Для new надо свой быстрый и не подверженный фрагментации менеджер памяти сделать. Если только для вышеописанных целей, то это будет еще больше накладных. А вообще такой мененжер памяти местами очень полезен. Надо будет сделать, как руки дойдут.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Apr 7 2010, 04:07
|

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

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

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

|
Цитата(AHTOXA @ Apr 7 2010, 11:22)  Если new будет использоваться только как замена глобальным объектам, то фрагментация ему не грозит, ибо будет только new, без delete. А тогда смысла нет в динамическом управлении памятью. Смысл ведь в том, чтобы юзать одну и ту же память под разные цели в разные моменты времени. Как стек, только со временем жизни объектов, определяемым пользователем. А если без delete, то тот вариант во статиками внутри получается функционально почти такой же, а по накладным лучше.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Apr 7 2010, 07:51
|

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

|
Цитата(dxp @ Apr 7 2010, 12:39)  А тогда смысла нет в динамическом управлении памятью. Смысл ведь в том, чтобы юзать одну и ту же память под разные цели в разные моменты времени. Смысл в том, что объекты создаются в нужном мне порядке, я управляю этим. Насчёт увеличения накладных - это вряд ли. ну разве что при создании объекта. При работе - простейшая проверка на null и возврат. Естественно, если будут нормальные new/delete, то лучше использовать их.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
Сообщений в этой теме
AndreyS Таблица переходов вместо Switch Mar 23 2010, 15:28       Dog Pawlowa Цитата(AndreyS @ Mar 24 2010, 00:14) ага.... Mar 23 2010, 20:55        Dog Pawlowa Цитата(AHTOXA @ Mar 24 2010, 01:54) Это о... Mar 23 2010, 22:02        RobFPGA Приветствую!
Цитата(AHTOXA @ Mar 24 201... Apr 5 2010, 20:36                  dxp Цитата(AHTOXA @ Apr 7 2010, 15:06) Смысл ... Apr 7 2010, 13:35                   AHTOXA А, это конечно. Просто наличие new позволяет даже ... Apr 7 2010, 13:44                    dxp Цитата(AHTOXA @ Apr 7 2010, 20:59) Кодif ... Apr 8 2010, 03:28   AHTOXA Цитата(AndreyS @ Mar 24 2010, 00:10) Меня... Mar 23 2010, 19:38   sonycman Цитата(AndreyS @ Mar 23 2010, 23:10) опти... Mar 27 2010, 05:48    AndreyS Цитата(sonycman @ Mar 27 2010, 09:48) Как... Mar 29 2010, 08:32   defunct Цитата(AndreyS @ Mar 23 2010, 22:10) Тепе... Apr 4 2010, 23:56    Dog Pawlowa Цитата(defunct @ Apr 5 2010, 02:56) А что... Apr 5 2010, 10:24     defunct Цитата(Dog Pawlowa @ Apr 5 2010, 13:24) Я... Apr 6 2010, 01:40      Dog Pawlowa Цитата(defunct @ Apr 6 2010, 04:40) А для... Apr 6 2010, 07:02 baralgin Цитата(Dog Pawlowa @ Mar 23 2010, 18:30) ... Mar 23 2010, 17:08 Dog Pawlowa Цитата(baralgin @ Mar 23 2010, 20:08) По ... Mar 23 2010, 17:45 zltigo Цитата(baralgin @ Mar 23 2010, 20:08) По ... Mar 23 2010, 17:58 baralgin Dog Pawlowa, zltigo, Спасибо, буду знать, а то бол... Mar 23 2010, 19:18
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|