[oodisc] OOo Basic - динамические переменные: как правильно описать и использовать

Alexej Kryukov akrioukov на newmail.ru
Пт Янв 28 00:07:27 MSK 2005


On Thursday 27 January 2005 17:43, Владислав Орлов wrote:

> > Чувствуется, что Вы никогда не имели дела с MS VB(A) :)
> Точнее - давно не имел дела. Уже больше трех месяцев. С тех самых 
> пор, как предложил руководству отказаться от услуг M$-БГ. Вот теперь 
> и осваиваю новые горизонты...

Ну, так это совсем недавно. Дурные привычки так быстро не забываются ;-)
Я, собственно, вот о чем: рассуждая о диалектах Бейсика, надо VB
брать за эталон, и отклонения по большей части считать багами. А VB,
подобно скриптовым языкам, сделан так, чтобы программисту вообще не
нужно было задумываться, откуда берется память. Поэтому до меня
с некоторым опозданием дошло, что Ваш пример об этом задуматься
таки заставляет, а всё именно потому, что вот таких самых багофич
он вскрывает по самое не хочу.

И, во-первых, два фундаментальных отличия, из-за которых Ваш код
не будет работать в VB:

а) Вы пользуетесь тем, что аргументы в OOo Basic по умолчанию
передаются ByRef (иначе надо было бы сделать, чтобы MakeTree
возвращала какое-то значение);

б) В VB запрещены рекурсивные определения структур, так что с Вашим
elementOfTreeType ничего бы не вышло. Могу догадаться, почему этого
запрета нет в OOo Basic: предположим, что существуют пользовательские
типы a и b, ссылающиеся друг на друга, причем тип b определен в
библиотеке, подключаемой по ходу работы программы. Тогда отследить
кольцевую зависимость на этапе компиляции головного модуля будет
невозможно, т. е. запрет всё равно нельзя было бы соблюсти.

Вообще говоря, это IMHO означает, что в таком стиле на OOo Basic
лучше не писать :) Но из этого вытекает остальное.

Итак, что происходит, если одним из членов структуры является
другая структура? Возьмем такой модуль (с _двумя_ пользовательскими
типами, чтобы обеспечить работоспособность в обоих Бейсиках):

Type MyType1
	x As Integer
End Type

Type MyType2
	x As Integer
	y As MyType1
End Type

Sub Test ()
	Dim a As MyType2

	MsgBox a.x
	MsgBox a.y.x
End Sub

a.x отображается в обоих случаях. Значит, действительно при объявлении
переменной отводится место под структуру и ее члены инициализируются
нулями. А вот a.y.x работает только в VB. Значит, VB сразу 
инициализирует дочерние структуры на всю глубину (а отчего бы не
сделать, если рекурсия исключена?), а вот OOo Basic начиная со второго
уровня создает только пустые ссылки. Иначе и нельзя, если мы не хотим
сожрать всю память в бесконечном цикле. Кстати, замечу, что при
связывании такой ссылки с объектом тип его не проверяется: можно
присвоить что-то совершенно левое и не относящееся к заявленному
типу.

Теперь вопрос: что происходит при присвоении значения структуре
второго уровня? Перепишем нашу функцию:

Sub Test ()
	Dim a As MyType2
	Dim b As MyType1
	
	b.x = 1
	a.y = b
	a.y.x = 2

	MsgBox b.x
End Sub

VB показывает цифру 1. Значит, при присвоении значения из одного
экземпляра структуры были скопированы в другой, после чего тот
зажил самостоятельной жизнью. OOo Basic показывает 2: следовательно,
при присвоении мы просто связали ссылку с уже существующим объектом.

Теперь предположим, что дочерняя структура будет объявлена
в другой функции (как в Вашем случае). Допустим:

Sub Test ()
	Dim a As MyType2
	Test1 (a)
	MsgBox a.y.x
End Sub

Sub Test1 (a As MyType2)
	Dim b As MyType1
	a.y = b
	a.y.x = 2
End Sub

Переменная b объявлена в процедуре Test1 и, следовательно,
должна быть очищена после ее завершения. Но как же ее
очистить, если мы связали ее со ссылкой, которая действует
в рамках функции Test(), а та еще не закончилась? Очевидно,
что в такой ситуации можно уничтожить только *ссылку* b, но
не саму область памяти, на которую она ссылается.

Естественно, что при таком подходе OOo Basic должен иметь
некий механизм garbage collection, т. е. автоматического 
уничтожения тех объектов, на которые нет больше ссылок. А поскольку
в Бейсике нет способа уничтожить уже объявленную переменную
до выхода из области ее действия, то, очевидно, именно на границах
таких областей (т. е. функций и процедур) этот механизм и должен
отрабатывать.

Так вот, полагаю, что в этом и должен заключаться ответ на
Ваш вопрос. Впрочем, если Вы в состоянии изложить все эти соображения
по-английски, то было бы любопытно задать вопрос в рассылку
udk at dev.openoffice.org (или, если не ответят, то 
api at dev.openoffice.org). Полагаю, что единственный человек,
который сможет точно сказать, чтО там происходит -- это
Andreas Bregas <andreas.bregas at sun.com>, ответственный за
разработку OOo Basic.

> Ну да... И нарваться на ошибку времени выполнения - "недостаточный
> размер стека..."...

Не совсем понял это замечание... Если переменная статическая, то,
значит, она *одна* во всех экземплярах функции, и, подобно переменным
уровня модуля, будет существовать, как минимум, до конца выполнения
программы.

> Или все наши порождения живут не до выхода из "процедуры, где были
> объявлены, и только пока она выполняется", а до выхода из MAIN'а? Или
> они вообще живут вечно (ну, пока ООо запущен)? И где тогда взять
> мозгов, чтобы проработать день ни разу не перезапустив программу? То
> есть - чиститься за собой нужно?..

Я на это, в принципе, ответил: полагаю, что рубежом может быть
любой выход из любой процедуры, после которого не останется ссылок
на область памяти. И уж точно она должна быть очищена после завершения
программы, если только нас не угораздило связать ее с переменной,
объявленной как Global.

> > Поскольку Вы ее вызываете рекурсивно, то -- да, конечно, создается.
> > А как же иначе? Не нравится -- объявляйте переменную как Static.
>
> Ну, про это мы уже говорили - нарвемся на "глухой" цикл при обходе
> дерева, состоящего из одного только корня...

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

> > Что он там делает -- бес его знает, но, во всяком случае,
> > стандартных способов удаления объекта не предусмотрено.
>
> Какой-то dispose() явно есть... Но все примеры, к нему относящиеся,
> начинались с того самого "com.sun.star.чего-то-там"... А я в ЭТОМ
> пока плаваю (сильнее, чем во всем остальном :-) )

У ЭТОГО есть корректное название: объекты UNO. И всё, что Вы с такими
объектами делаете, делается *не* Бейсиком и к его синтаксису не имеет
ровным счетом никакого отношения: Бейсик тут выступает только
передаточным звеном.

К тому же метод dispose свойствен далеко не всем объектам UNO, и служит,
как правило, вовсе не для очистки памяти (хотя она при этом и может
очищаться), а для каких-то практических целей. Например, уничтожение
открытого окна с загруженным в него документом.

-- 
Regards,
Alexej Kryukov <akrioukov at newmail dot ru>

Moscow State University
Historical Faculty


Подробная информация о списке рассылки Oo-discuss