Тайна потусторонних связей или Использование переадресованных вызовов

Файлы

В далеком, теперь уже, 1994 году, в свете развития всяких ООП технологий , в том же Turbo Vision, захотелось и мне в Фоксе придумать нечто такое же, пусть, хоть и отдаленно напоминающее сие новаторства. Собственно все сводилось к возможности получения результата функции из внешнего, по отношению к текущему, модуля. Если разработчики дали возможность использовать конструкции DO Func1 in Module1

То почему-то не дали возможности хотя бы сделать так Do Func1 in Module1 to myVar

Хотя и такая конструкция, меня не устраивала. Хотелось сделать модуль отвечающий за поведение определенного вида документа, но как получать результат функции их этого модуля или другого без шаманского передергивания с использованием Set Procedure

To, которая тогда позволяла использовать только один общий процедурный модуль. Тогда надо было выносить все мелкие общие функции из одного модуля и раскладывать их на кучу мелких. Это мне тоже не нравилось. Оставалось одно – пропадать.
Надо было как-то заставить эти функции держать в связке стека вызовов FoxPro иначе он никак их не хотел находить.
И вот пришло решение, что вызов функции нужно переадресовать тому модулю откуда он должен быть вызван, но как? Да все оказалось проще простого.
Пишем процедурный модуль MODULE1.PRG с небольшим «довеском» , допустим в нем есть некая функция P1* MODULE1.PRG
parameter m.__
if type(`m.__`)=`C`
return eval(m.__)
endif

Procedure P1
Return 1

Далее пишем некий главный файл MAIN.PRG (Привет Владимиру Максимову)
* MAIN.PRG
? MODULE1([P1()])

На выходе получаем искомое значение 1 которое вернула функция P1 – Ура, у нас получилось !
Изменяем модуль MAIN.PRG на такой
* MAIN.PRG
? MODULE1([P1()])

Procedure P1
Return 2

Проверим, что вернет нам наша «шаманская» функция ? А в результате видим – 1. Значит наша функция в MAIN.PRG не сработала ! И правильно, ибо вызов функции идет в модуле MODULE1.PRG в строке EVAL(m.__) – фокс находит функцию в текущем модуле и перестает ее искать где-то еще и возвращает значение нужной нам функции. Ну пробовать, так пробовать. Еще раз меняем файл MAIN.PRG
* MAIN.PRG
? MODULE1([P2()])

Procedure P2
Return 2

Прошу заметить, что зайца в шляпе нет, то есть функции P2 в MODULE1.PRG нет. Смотрим,что вернет функция.
Оказывается возвращает значение – 2. Пробуем штатными средствами FoxPro, пишем
DO P2 in MODULE1

И получаем ошибку: Procedure “P2” not found – чего собственно и ждали, но в нашем случае такой ошибки нет и функция вернула, значение из модуля MODULE1 , это что же получается Фокс нас обманывает ? Или все-таки мы его? .
Давайте подумаем, а собственно откуда в MODULE1.PRG взялось значение функции P2 ?
Фокс в модуле MODUL1.PRG пытается найти функцию P2 – не находит и переходит по стеку вызовов на ранние вызовы, и что же, находит ее в модуле MAIN.PRG – соответственно значение этой функции он нам и вернет. Проблема побеждена, и в том же 1994 году за полторы недели на коленке был написан склад с использование псевдо-объектности. Жаль исходники потерял, за давностью лет. Но было здорово и быстро.

Ну вот, а теперь собственно — Амбула.

Был у меня некий проект на VFP6 – размер исполняемого файла 3.5 метра – все в одном. Решил я это дело порезать на куски. Ну общие процедурные модули и дополнительные невизуальные классы вынести в отдельный модуль COMMON.APP – сказано – сделано – исполняемый файл уменьшили на 1 метр, осталось 2.5 – из них картинок около 1.5 метров – на все случаи жизни, так сказать. А чего давай и их выкинем из исполняемого файла – вынесем куда-нибудь в IMAGES.APP и пусть там валяются, все равно этот модуль – ну очень редко будет изменяться. Ладно собираем все картинки в новый IMAGES.APP в проекте им даем атрибут Excluded – собираем главный исполняемый файл, предварительно в нем подключаем модуль IMAGES.APP. А вместо картинок видим крестики, это что же теперь на картинках крест поставить ? Ладно начнем думать. Если так картинки не подключаются, давай старым дедовским шаманским способом – переадресуем подключение картинок в IMAGES.APP
В базовом классе Image дописываем в свойство Picture_Assing следующий код
LPARAMETERS vNewVal
*To do: Modify this routine for the Assign method
THIS.Picture = m.vNewVal
if not empty(this.picture)
* Если файл не найдет в исполняемом файле или на диске – переадресуем вызов
if not file(this.picture)
if file(“IMAGES.APP”)
DO “IMAGES.APP” with (this),`picture`,this.picture
endif
endif
endif

Соответственно меняем главный файл для проекта IMAGES, а как без главного модуля ? Даже пустого – APP не слепить.
Процедура может принимать от 3 параметров
1 – ссылка на объект к которому будем цеплять картинку
2 – имя свойства, в нашем случае PICTURE
3 – собственно имя файла картинки
4 – параметр может использоваться для ListBox или ComboBox для свойства Picture(N) – как N
lparameter m.ObjRef, m.cProperty, m.xValue, m.nIndex
if pcount() < 3
MessageBox(«Это модуль ресурсов»,64,_screen.caption)
Return .F.
endif
if vartype(m.ObjRef)=`O` ;
and vartype(m.cProperty)=`C` ;
and PEMStatus(m.ObjRef,m.cProperty,5)

if vartype(m.nIndex)=`N`
if m.cProperty=`PICTURE`
ObjRef.Picture[m.nIndex]=m.xValue
else
Return ObjRef.WriteExpression(m.cProperty+`[m.nIndex]`,[m.xValue])
endif
else
Return ObjRef.WriteExpression(m.cProperty,[m.xValue])
endif
Return .T.
endif

Return .F.

Теперь пересобираем APP для картинок – и главный исполняемый файл – и видим все картинки на месте. Ну вот – наконец-то смогли уменьшить размер главного исполняемого файла еще на 1.5 метра – остался метр «живого» кода – теперь можно вздохнуть спокойно.

Используя «потусторонние связи» с переадресацией вызова, оказывается можно много чего получить в Фоксе