|
Таблица переходов вместо 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 надо свой быстрый и не подверженный фрагментации менеджер памяти сделать. Если только для вышеописанных целей, то это будет еще больше накладных. А вообще такой мененжер памяти местами очень полезен. Надо будет сделать, как руки дойдут.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
Сообщений в этой теме
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               AHTOXA Цитата(dxp @ Apr 7 2010, 10:06) Для new н... Apr 7 2010, 04:07                dxp Цитата(AHTOXA @ Apr 7 2010, 11:22) Если n... Apr 7 2010, 06:24                 AHTOXA Цитата(dxp @ Apr 7 2010, 12:39) А тогда с... Apr 7 2010, 07:51                  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
|
|
|