vsb>Сделать отдельную структуру-очередь. К примеру на основе кольцевого списка. Далее один модуль в него добавляет оповещение. У других модулей есть функция вроде process_events(), которая вызывается где-то в основном цикле. Эта функция проверяет наличие оповещений и обрабатывает их, если они есть. vsb>Т.е. развернуть модель работы с push на poll.
Нету главного цикла.
Есть только один поток. Пользователь, грубо говоря нажал кнопку, и вызывается процедура, которая вызывает другие процедуры...
И все это в одном потоке до возвращения управления пользователю.
vsb>Второй вариант — реализовать концепцию указателей на функции поверх существующих средств, в прошлой подобной теме я уже про это писал, повторяться не буду.
Как раз интересен этот вариант.
А в какой теме вы это писали?
Если имеется ввиду моя старая тема, то там вы не написали как это можно сделать.
Вот ваш ответ: "Изобрести указатель на функцию можно, но не нужно."
Z>А версия языка, которую я сейчас использую — это еще более древняя версия, которая даже не умеет работать с несколькими модулями. Z>То есть программа — это один монолитный большой программный файл-модуль.
Z>Но это не исключает того, что этот модуль надо организовать так, чтобы методы были разделены по обязанностям. Чтобы между методами, относящимися к разным обязанностям, было как можно меньшее связывание. Z>Так как по объему кода, по количеству функций, этот модуль получится достаточно большой. Z>Без "логичного" разделения в нем будет трудно ориентироваться.
Ну раз нет возможности использовать полиформизм на уровне кода, значит придётся использовать полиморфизм на уровне данных.
В качестве примера можно подсмотреть стек TCP, виндовый WinAPI — структуры переменного размера, в начале размер и тип данных. Внутри обработчика анализ заголовка и передача по цепочке. Т.е. в какой-то точке программы будут собраны все обработчики. Это не хорошо, но и не плохо. Чудес не бывает, какие-то накладные расходы всегда будут.
Наконец, вспомнить, как работал Int 21h
Если есть доступ к ОСи (надеюсь, язык не совершенно оторван от реалий), то можно использовать системозависимые механизмы для обмена данными, выше камрады об этом упомянули.
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, zelenprog, Вы писали:
Z>Только в той старой теме я использовал более "продвинутую" (более позднюю) версию "моего" языка, которая умела работать с несколькими модулями.
Перепишите уже "свой" язык и добавьте то чего вам нехватает
Z>А версия языка, которую я сейчас использую — это еще более древняя версия, которая даже не умеет работать с несколькими модулями.
o_O архиология под дос?
Всё таки я склоняюь к мысли что был Visual Basic 6.0
А стал Visual Basic 1.0:
Z>То есть программа — это один монолитный большой программный файл-модуль.
И что плохого?
Z>Без "логичного" разделения в нем будет трудно ориентироваться.
Так разделите логично
Z>Вот чтобы в этой программе получилось слабое связывание между процедурами, нужно придумать как в этом процедурном языке "внедрять" одну процедуру в другую.
??? Зачем. Пересылайте сообщения как в objective-c
Z>Например, есть процедура, которая выполняет "бизнес"-операцию. Z>Эта процедура "оповещает" о выполненных действиях. На эти оповещения должен отреагировать другой код.
Так и посылайте оповещения
Z>Для простоты рассмотрим интерфейс: на оповещения интерфейс должен перерисоваться соответствующим образом. Z>то есть имеется процедура, которая отображает что-то на форме в зависимости от оповещения о выполненном дейтсвии.
Вы телегу впереди лошади ставите. Есть модель которую кто-то меняет, а есть слушатели изменений и по изменения они изменяют то что отображается.
Z>Смысл в том, что потом потребуется создать еще один (третий) вид интерфейса.
Z>И хорошо было бы, чтобы создав новую процедуру для нового интерфейса, например "GUI_Update3()", реализующую новый способ "реакции", я поменял бы только код вызывающей процедуры, добавив новое условие:
В процессе не должно быть вызовов GUI_Update1, только notifyUpdate
Более того если у вас длительные процессы вам надо переделать ваши процессы на итерационные.
Z>И чтобы мне не пришлось менять ни бизнес-процедуру, ни других частей программы.
Так и в чем проблема? Вы не придумали как имеющимся средствами сделать по феншую?
ps: вы хотя бы примеры кода на своём загадочном языке привели бы, или названия, что бы было понятно какие ограничения есть и как их обойти.
Z>>То есть программа — это один монолитный большой программный файл-модуль. _>И что плохого?
А я не говорю, что это плохо.
Просто надо в такой программе написать хороший код, предполагающий слабое связывание. Z>>Без "логичного" разделения в нем будет трудно ориентироваться. _>Так разделите логично
Логично я разделил.
Но кроме этого, еще нужно реализовать слабое связывание. Z>>Вот чтобы в этой программе получилось слабое связывание между процедурами, нужно придумать как в этом процедурном языке "внедрять" одну процедуру в другую. _>??? Зачем.
Затем, чтобы бизнес-процедура, меняющая данные, не зависела от изменений в процедуре, отображающей эти данные.
Или от добавления в программу новой процедуры, отображающей данные. _>Пересылайте сообщения как в objective-c _>Так и посылайте оповещения
Я не знаю objective-c
Но судя по описанию в википедии, это объектно-ориентированный язык.
Мой язык — процедурный. В нем нету ни сообщений, ни оповещений.
Как в процедурном языке посылать сообщения и оповещения? Z>>Для простоты рассмотрим интерфейс: на оповещения интерфейс должен перерисоваться соответствующим образом. Z>>то есть имеется процедура, которая отображает что-то на форме в зависимости от оповещения о выполненном дейтсвии. _>... Есть модель которую кто-то меняет, а есть слушатели изменений и по изменения они изменяют то что отображается.
Ну да. Это я и имел ввиду.
Может быть не точно выразился. Z>>Смысл в том, что потом потребуется создать еще один (третий) вид интерфейса.
... _>Так и в чем проблема? Вы не придумали как имеющимся средствами сделать по феншую?
Да, не придумал. Ну как-то наколбасить, конечно, можно.
Но меня интересует как это сделать "правильно". _>ps: вы хотя бы примеры кода на своём загадочном языке привели бы, или названия, что бы было понятно какие ограничения есть и как их обойти.
Все ограничения я уже подробно описал.
Конкретное название языка к понимаю проблемы ничего не добавит.
Примеры кода можно писать на любом удобном псевдо-языке.
Все будет понятно. Например я выше написал простой код.
Напишу его более подробно:
Код
// эта процедура срабатывает по инициативе пользователя,
// который запускает какую-то обработку,
// например, нажимает красную кнопку на приборе
void GUI_Event_StartProcess()
{
// сначала определяется какая конкретная процедура должна вызываться
// при возникновении события в бизнес-процедуре
// в зависимости от условий и параметров
if (...)
// при этих уловиях должна срабатывать GUI_Update_1:
// оповещения в консоль
elseif (...)
// при этих уловиях должна срабатывать GUI_Update_2:
// оповещения в виде сигнальных лампочек
// вызываем бизнес-процедуру
BL_DoProcess();
}
void GUI_Update_1()
{
// первый способ отображения изменений модели,
// например вывод в консоль
Text = ....
}
void GUI_Update_2()
{
// второй способ отображения изменений модели,
// например включаем соответсвующие сигнальные лампочки
switch_on(lamp1);
}
void BL_DoProcess()
{
while(...)
{
...
// обработка данных
...
BL_notifyUpdater();
}
}
void BL_notifyUpdater()
{
// что здесь должно быть, чтобы вызывалась
// конкретная процедура GUI_Updater_xxx?
}
SVZ>Ну раз нет возможности использовать полиформизм на уровне кода, значит придётся использовать полиморфизм на уровне данных. SVZ>В качестве примера можно подсмотреть стек TCP, виндовый WinAPI — структуры переменного размера, в начале размер и тип данных. Внутри обработчика анализ заголовка и передача по цепочке. Т.е. в какой-то точке программы будут собраны все обработчики.
А можно простой пример?
Чтобы было понятнее?
SVZ>Если есть доступ к ОСи (надеюсь, язык не совершенно оторван от реалий), то можно использовать системозависимые механизмы для обмена данными, выше камрады об этом упомянули.
Через файлы обмен будет очень медленным — это самый крайний вариант.
Здравствуйте, zelenprog, Вы писали: Z>В том-то и дело, что в "моем" языке нету указателей на процедуру. Z>Как выкрутиться в этой ситуации?
Тогда ваш единственный выход — switch.
То есть примерно так:
int BinaryOp(int a, int b, int functionNo) // весь полиморфизм - здесь
{
switch(functionNo)
{
case 0: return Add(a, b);
case 1: return Mul(a, b);
case 2: return Sub(a, b);
default: throw new Exception("Unknown function no: " + functionNo.ToString());
}
}
int Foo(int[] data, int initValue, int functionNo)
{
var acc = initValue;
for(var item in data)
acc = BinaryOp(acc, item);
return acc;
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
SVZ>>Ну раз нет возможности использовать полиформизм на уровне кода, значит придётся использовать полиморфизм на уровне данных. SVZ>>В качестве примера можно подсмотреть стек TCP, виндовый WinAPI — структуры переменного размера, в начале размер и тип данных. Внутри обработчика анализ заголовка и передача по цепочке. Т.е. в какой-то точке программы будут собраны все обработчики.
Z>А можно простой пример? Z>Чтобы было понятнее?
Для множества уведомлений set/get используется единая схема.
Шлётся сообщение WM_NOTIFY, в качестве параметра — указатель на структуру с данными.
Все структуры имеют одинаковую структуру:
Первым идёт заголовок NMHDR, в нём лежит код уведомления.
В зависимости от кода уведомления указатель на void кастится к нужной структуре.
Т.е. все твои отправители уведомлений используют одну и ту же функцию для отправки сообщений.
Если концепция указателей в твоём языке отсутствует, то массив байт создать можно? В этом случае, чтобы отправить уведомление надо выделить блок памяти нужного размера, сериализовать в него свою структуру с данными, массив отправить в диспетчер. Диспетчер десериализует заголовок, подсмотрит тип сообщения и вызовет нужную функцию, которая уже десериализует структуру целиком и обработает уведомление.
Т.е. тут у тебя диспетчер знает о всех обработчиках, но зато остальные части приложения знают только о диспетчере.
в Си все эти преобразования происходят без копирования памяти. А в твоём случае —
SVZ>>Если есть доступ к ОСи (надеюсь, язык не совершенно оторван от реалий), то можно использовать системозависимые механизмы для обмена данными, выше камрады об этом упомянули.
Z>Через файлы обмен будет очень медленным — это самый крайний вариант.
А через сокет? Работа с localhost (наверное в 99% операционок) реализована через память (внутри драйвера), без обращения к железу.
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, zelenprog, Вы писали:
vsb>>Второй вариант — реализовать концепцию указателей на функции поверх существующих средств, в прошлой подобной теме я уже про это писал, повторяться не буду.
Z>Как раз интересен этот вариант. Z>А в какой теме вы это писали? Z>Если имеется ввиду моя старая тема, то там вы не написали как это можно сделать. Z>Вот ваш ответ: "Изобрести указатель на функцию можно, но не нужно."
Тут лучше бы больше знать про то, что доступно в языке.
Самый простой вариант это что-то вроде
function call_by_pointer(pointer: integer, argument: integer): integer {
if (pointer == 1) return function_1(argument);
if (pointer == 2) return function_2(argument);
if (pointer == 3) return function_3(argument);
throw error;
}
Т.е. у нас указатель это некое число и при передаче его в функцию call_by_pointer мы вызываем целевую функцию. Далее в тех местах, где нужен указатель на функцию — мы храним это число, а при необходимости вызвать функцию мы вызываем call_by_pointer.
Если в языке есть строки, может быть лучше заменить число на строку, к примеру банально — названию функции:
function call_by_name(name: string, argument: integer): integer {
if (name == "function_1") return function_1(argument);
if (name == "function_2") return function_2(argument);
if (name == "function_3") return function_3(argument);
throw error;
}
}
Для каждого сочетания аргументов и возвращаемого значения нужно будет делать новую такую функцию.
Этот подход можено улучшать в зависимости от того, что ещё в языке есть.
SVZ>... чтобы отправить уведомление надо ... структуру с данными , массив отправить в диспетчер. SVZ> Диспетчер десериализует заголовок, подсмотрит тип сообщения и вызовет нужную функцию, которая уже десериализует структуру целиком и обработает уведомление. SVZ>Т.е. тут у тебя диспетчер знает о всех обработчиках, но зато остальные части приложения знают только о диспетчере.
Понятно, спасибо.
То есть суть решения — в диспетчере.
Диспетчер — это будет отдельная процедура, которая будет знать типы сообщений и вызывать соответствующий обработчик.
Так и будет выглядеть.
Это же просто вызов двух методов. Другой вопрос как они реализованы.
Но их использование будет выглядеть именно так как вы написали. _>Как выглядит объявление стуктур и объявления переменных? _>Как будет выглядеть подобный код на вашем языке?
Все это можно реализовать на моем языке. Массивы, структуры (в урезанном виде) есть.
За исключением следующих моментов:
1) Нету указателей на функции. То есть вот этот код нельзя реализовать:
int fn4( int (*fn)(int) ) {
return fn(7);
}
int fn5() {
return fn4(fn3);
}
2) Нету строгой типизации. Переменные, параметры и возвращаемые значения функций могут быть любого типа.
3) Все параметры методов по умолчанию передаются по ссылке.
Нет необходимости специально писать вот так:
"int fn1(State* state)" — вместо этого можно написать просто "func fn1 (pState)"
"return fn1(&s[0]) + fn1(&s[1]) + global" — можно написать "return fn1(s[0]) + fn1(s[1]) + global"
Можно параметры передавать по значению.
Но для передачи по значению писанины больше, и так редко делается, редко возникает такая необходимость.
Здравствуйте, zelenprog, Вы писали:
SVZ>>... чтобы отправить уведомление надо ... структуру с данными , массив отправить в диспетчер. SVZ>> Диспетчер десериализует заголовок, подсмотрит тип сообщения и вызовет нужную функцию, которая уже десериализует структуру целиком и обработает уведомление. SVZ>>Т.е. тут у тебя диспетчер знает о всех обработчиках, но зато остальные части приложения знают только о диспетчере.
Z>То есть суть решения — в диспетчере. Z>Диспетчер — это будет отдельная процедура, которая будет знать типы сообщений и вызывать соответствующий обработчик.
Как минимум функция, возможно, несколько функций.
Если потребуется асинхронность, то нужна будет очередь (структура данных) и функция, которая будет эту очередь проворачивать в правильный момент (В WinAPI это связка функций GetMessage/PeekMessage + DispatchMessage).
_____________________
С уважением,
Stanislav V. Zudin
vsb>Т.е. у нас указатель это некое число и при передаче его в функцию call_by_pointer мы вызываем целевую функцию. Далее в тех местах, где нужен указатель на функцию — мы храним это число, а при необходимости вызвать функцию мы вызываем call_by_pointer. vsb>Если в языке есть строки, может быть лучше заменить число на строку, к примеру банально — названию функции:
vsb>Для каждого сочетания аргументов и возвращаемого значения нужно будет делать новую такую функцию. vsb>Этот подход можено улучшать в зависимости от того, что ещё в языке есть.
Идею понял. Спасибо.
Выше писали что-то похожее про диспетчер.
Если провести аналогию с ООП (внедрение зависимости), то получается следующая картина.
Допустим есть рабочий код (процедура), которая в ходе выполнения должна вызывать какой-то конкретный "внедренный" метод-обработчик (процедуру).
Для этого рабочий код вызывает что-то типа процедуры "call_by_name".
В данном случае "call_by_name" — по сути является "интерфейсной" процедурой.
А реальная процедура, которая будет вызываться в зависимости от параметра — это внедренная процедура.
Клиентский код для "внедрения" конкретной процедуры в рабочий код, передает в рабочий код параметр, обозначающий конкретную процедуру.
Верно?
Процедуру "call_by_name" можно назвать "интерфейсной" процедурой или "базовой" процедурой (процедурой базового класса).
Получается эта "базовая" процедура должна знать названия всех процедур-"наследников".
S>Тогда ваш единственный выход — switch. S>То есть примерно так:
S>
S>int BinaryOp(int a, int b, int functionNo) // весь полиморфизм - здесь
S>{
S> switch(functionNo)
S> {
S> case 0: return Add(a, b);
S> case 1: return Mul(a, b);
S> case 2: return Sub(a, b);
S> default: throw new Exception("Unknown function no: " + functionNo.ToString());
S> }
S>}
S>int Foo(int[] data, int initValue, int functionNo)
S>{
S> var acc = initValue;
S> for(var item in data)
S> acc = BinaryOp(acc, item);
S> return acc;
S>}
S>
Понял, спасибо.
Очень похожие решения также написали выше (про диспетчер, хотя суть немного отличается) и ниже (call_by_name). https://rsdn.org/forum/design/8788561.1
SVZ>Если потребуется асинхронность, то нужна будет очередь (структура данных) и функция, которая будет эту очередь проворачивать в правильный момент (В WinAPI это связка функций GetMessage/PeekMessage + DispatchMessage).
Асинхронность конечно хорошо бы добавить. Но боюсь, что это невозможно.
Так как в этой программе есть только один поток.
Пользователь инициирует запуск программы, программа ждет ввода пользователя, пользователь например нажимает кнопку, и вызывается процедура, которая вызывает другие процедуры... И все это в одном потоке до возвращения управления пользователю.
SVZ>>Если потребуется асинхронность, то нужна будет очередь (структура данных) и функция, которая будет эту очередь проворачивать в правильный момент (В WinAPI это связка функций GetMessage/PeekMessage + DispatchMessage).
Z>Асинхронность конечно хорошо бы добавить. Но боюсь, что это невозможно. Z>Так как в этой программе есть только один поток.
Как раз это не проблема.
Z>Пользователь инициирует запуск программы, программа ждет ввода пользователя, пользователь например нажимает кнопку, и вызывается процедура, которая вызывает другие процедуры... И все это в одном потоке до возвращения управления пользователю.
Раз есть окошко, значит есть какой-то цикл обработки сообщений (обработчики нажатий кнопок откуда-то же вызываются).
Значит можно в этом цикле вызвать твою функцию, которая будет дергать обработчики сообщений из твоей собственной очереди.
_____________________
С уважением,
Stanislav V. Zudin
SVZ>Раз есть окошко, значит есть какой-то цикл обработки сообщений (обработчики нажатий кнопок откуда-то же вызываются). SVZ>Значит можно в этом цикле вызвать твою функцию, которая будет дергать обработчики сообщений из твоей собственной очереди.
Цикл обработки сообщений есть, но он не доступен для разработчика программы.
Вмешаться в него штатными средствами невозможно.
Я читал, что есть "примочки", которые позволяют сделать вызов какой-то процедуры по таймеру.
Но это все делается через подсовывание хакнутых dll-модулей в runtime-среду этого языка.
Пока что не хочется с этим связываться.
SVZ>>Раз есть окошко, значит есть какой-то цикл обработки сообщений (обработчики нажатий кнопок откуда-то же вызываются). SVZ>>Значит можно в этом цикле вызвать твою функцию, которая будет дергать обработчики сообщений из твоей собственной очереди.
Z>Цикл обработки сообщений есть, но он не доступен для разработчика программы. Z>Вмешаться в него штатными средствами невозможно.
А точно нет никаких callback'ов? Типа обработчика OnIdle? Или хотя бы таймера7
_____________________
С уважением,
Stanislav V. Zudin
SVZ>А точно нет никаких callback'ов? Типа обработчика OnIdle?
Нету
SVZ>Или хотя бы таймера7
Таймер можно сделать с помощью подмены в среде выполнения хакнутой dll-ки.
Но так мне не хочется делать.
Хочется по максимуму использовать штатные средства.
Здравствуйте, zelenprog, Вы писали:
Z>Как это делается в процедурных языках?
Да назовите вы язык то. Хватит стесняться. Тут и люди, которые к мейнфреймам 70-х готов web api прикручивали есть, так что легаси это не страшно. Это просто больно.