OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Пятница, 26 Апрель, 2024 16:28

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 38 ]  На страницу 1, 2  След.
Автор Сообщение
 Заголовок сообщения: Проблемы с передачей массивов
СообщениеДобавлено: Среда, 22 Февраль, 2006 11:27 

Зарегистрирован: Среда, 22 Февраль, 2006 10:35
Сообщения: 144
Откуда: Новочеркасск
Здравствуйте! Подскажите, кто знает, как решить нижеописанную проблему. Итак... в BlackBox набран следующий модуль:
Код:
     MODULE MyDll;
     (* sample module to be linked into a dll *)

      PROCEDURE ArrayIncrement*(VAR x: ARRAY OF INTEGER);
      VAR i: INTEGER;
      BEGIN
          FOR i := 0 TO LEN(x) - 1 DO
            INC(x[i]);
         END;
      END ArrayIncrement;
      
     END MyDll.

Откомпилирован он в статическую dll командой
Код:
DevLinker.LinkDll
MyDll.dll := MyDll# ~

В программе на Delphi 7 процедура ArrayIncrement объявлена как
Код:
  PROCEDURE ArrayIncrement(VAR x: array of integer); stdcall; external 'MyDll.dll';

Использующая процедуру ArrayIncrement процедура:
Код:
procedure TForm1.Button2Click(Sender: TObject);
var i: integer;
    a: array [0..4] of integer;
begin
  for i:= 0 to 4 do
  begin
      a[i] := 0;
  end;
  ArrayIncrement(a);
end;

При вызове ArrayIncrement(a); генерируется исключение "External exception C000001D".
Как исправить эту ошибку? Как правильно передавать параметры в процедуры Компонентного Паскаля из процедур, написанных на других языках? Почему НИГДЕ не описан способ передачи параметров в Обероноподобных языках?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Проблемы с передачей массивов
СообщениеДобавлено: Среда, 22 Февраль, 2006 11:44 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 18:55
Сообщения: 2272
Откуда: Россия, Нижний Новгород
Ничего удивительного. Тип ARRAY OF INTEGER в Component Pascal - это одно (обобщенный value-тип всех целочисленных массивов располагаемых в любых типах памяти), а тип array of integer в Delphi - это совсем пересовсем абсолютно другое (reference-тип динамического целочисленного массива). Поскольку в Delphi нет аналога типа ARRAY OF INTEGER, то единственный способ "передать" массив из Delphi в Blackbox это передать адрес нулевого элемента. А в Blackbox-е "поколдовать" с ним посредством SYSTEM.

Андрей писал(а):
Почему НИГДЕ не описан способ передачи параметров в Обероноподобных языках?

Всё описано. В Blackbox-е смотрите раздел Platform-Specific Issues.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 12:58 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1429
Код:
  PROCEDURE ArrayIncrement*(VAR x: ARRAY [untagged] OF INTEGER);

Однако, теперь LEN для x будет недоступна.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 13:21 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Ваша проблема, так сказать о двух концах:

1) В ББ ARRAY OF INTEGER - это ТЭГОВЫЙ массив, т.е. массив, перед которым идет тег его типа (статического или, если массив выделен динамически, временного описателя). Поэтому к нему, например, можно применять LEN(a) - возвращается размер этого массива. Такие массивы можно передавать только внутри одного ББ - даже между двумя DLL, написанными на ББ - уже нельзя, т.к. сборщики мусора-то у них разные.

Ваш модуль должен выглядеть так:

MODULE MyDll;
(* sample module to be linked into a dll *)

IMPORT SYSTEM; (* Читайте Особенности платформы (Platform-Specific-Issues *)

(* Говорим, что массив безтеговый. Размер передаем явно, т.к. без тега определить размер открытого массива невозможно *)
PROCEDURE ArrayIncrement*(VAR x: ARRAY [untagged] OF INTEGER; n: INTEGER);
VAR i: INTEGER;
BEGIN
FOR i := 0 TO n DO
INC(x[i])
END
END ArrayIncrement;

END MyDll.

С INTEGER нет проблем с выравниванием. А если бы передавали ARRAY OF BYTE, то надо было бы явно указать [noalign] или другое выравнивание и то же самое в Дельфе.

2) В Дельфе опять же, свои заморочки.
VAR array of - открытый массив. По-моему, размер открытых массивов в Дельфе передается скрытым параметром в стеке, т.е. получаем несовместимость по параметрам.

Посему можно использовать один из следующих вариантов:

PROCEDURE ArrayIncrement(x: array of integer; n: integer); stdcall; external 'MyDll.dll';

(array of integer в Дельфе - это динамический массив, "указатель на массив" - что нам и надо)

или

Type IntArray = array [0..High(Integer) div 4 -1] of Integer;
pIntArray = ^IntArray;

procedure ArrayIncrement (x: pIntArray; n: integer); stdcall; external 'MyDll.dll';

procedure Test;
VAR x: array [0..256] of integer;
begin
ArrayIncrement(@x, Length(x)) - только @ не забывать ставить...
end;


Я не на 100% уверен, т.к. Дельфу подзабыл изрядно - но попробуйте оба варианта, хотя бы один должен сработать.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 14:08 

Зарегистрирован: Среда, 22 Февраль, 2006 10:35
Сообщения: 144
Откуда: Новочеркасск
Огромное спасибо за советы! :)
Почему у меня возникли такие вопросы? Дело в том, что мне (для диплома) необходимо написать модуль для обработки сигналов, основанной на нечеткой логике. Модуль должен быть каким-то образом включен в уже существующую программу, довольно специфичную, которая является виртуальным "пультом оператора". Программа эта написана на Дельфи. Именно легкость написания визуальных интерфейсов в этой среде, а также большая распространенность ее и компонентов к ней побудили использовать именно ее для работы над проектом. Программа была написана не мной, но с моим участием. (Я писал на C алгоритмы для объекта управления)
Однако, недавно я узнал о существовании Компонентного Паскаля, прочитал много хвалебной информации в адрес этого языка (кстати, действительно конструктивной критики не нашел) и решил писать программу именно на этом языке. Тут и возникли описанные выше проблемы :D :oops:
Так вот, я прошу Вашего совета, что мне лучше делать в данной ситуации: продолжить изучение Дельфи или Компонентного Паскаля? В BlackBox или в Delphi быстрее я добьюсь результата - работающую программу? Кстати, алгоритмы работы нечеткой логики уже готовы, то есть на бумаге на псевдоязыке расписано как и что должно выполняться! Заранее благодарю!


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 14:48 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1429
Исходный пример должен бы работать, если бы не одна "мелочь".
При вызове ArrayIncrement Delphi передает максимальный индекс, а ББ ожидает длину массива.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 14:52 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Ну, я, конечно, лицо заинтересованное, но все-таки постараюсь быть объективным :-)

Мой опыт работы на Дельфи - более 5 лет. Параллельно полтора года - С++. По поводу второго ничего говорить не буду, а то опять holly war начнется :-)... А по поводы Дельфи - КП как язык, по синтаксису, концепциям, безусловно, совершеннее и современнее. Язык Дельфи (начиная с Дельфи-7 Объектный Паскаль официально переименован в Дельфи) - это старая ветка Паскаль - Turbo Pascal - Object Pascal - и она волочит за собой соответствующую наследственность. Возможно, большая ошибка Борланд в том, что в свое время они решили развивать не уже существовавшую Модула-2, а старый Паскаль, по сути, заимствуя и во-многом ухудшая концепции из Модулы и Оберона. Что бы не говорили, ООП в Дельфи реализован не слишком удачно - объекты могут быть только динамическими, даже для малого временного объекта в процедуре необходимо прописывать := TMyObject.Create, а в конце не забывать вызывать Destroy. Из-за несогласованности этого процесса (новички, по крайней мере) постоянно наступают на грабли утечки памяти либо "access violation" по нулевому указателю. То, что в КП приличная часть данных (ОО-данных) обрабатывается статически, серьезно влияет на быстродействие (особенно это заметно в сравнении с той же Java, в которой все построено на динамике. Там очень сильно растет нагрузка на сборщик мусора) - например, в BlackBox весь механизм сообщения работает со статическими объектами. Лично мне очень нравится то, что не введено специально понятие Class, а используется обычный RECORD. Множество мелочей навроде более удобного написания операторов:
IF ... THEN
ELSE
END
вместо
if then
begin
end
else
begin
end - насколько легче, а?

Язык полностью совместим с Java. Существует несколько реализаций трансляторов с КП в Java-код. Сама Oberon Microsystems разработала на BlackBox'e операционку реального времени JBed - для встроенных систем, в частности. Так там вообще модули на КП и Яве работают вместе, разделяя общую память и сборщик мусора. Компилятор для Borland JBuilder также делала Oberon Microsystems по заказу Borland на Компонентном Паскале.

Наконец, сам язык очень стройный и красивый, нет никаких излишеств, но есть все, что нужно.

А если говорить о среде выполнения - то динамическая модульность и сборка мусора очень весомые аргументы в сравнении с Win32-Delphi. А Delphi .NET - это уже отдельная история...

И, наконец, последнее: Borland продает все свои разработки IDE. Если раньше речь шла только о "кризисе Дельфи", то сейчас можно говорить о том, что на ней поставлен крест.
BlackBox же продолжает развиваться самой компанией, и достпуен в исходных кодах, поэтому ближайшие годы за его судьбу можно не опасаться. Если же понадобиться работать под .NET или JVM - то есть пакет GPCP.

Так что, размышляйте, сравнивайте, решайте.

Цитата:
В BlackBox или в Delphi быстрее я добьюсь результата - работающую программу?

На моей практике программы в ББ получаются значительно более надежными, а ошибки обнаруживаются гораздо быстрее (не смотря на отсутсвие привычного многим пошагового отладчика). Кстати, встроенный профилировщик в ББ есть, поэтому оптимизировать также легче.


Последний раз редактировалось Илья Ермаков Среда, 22 Февраль, 2006 15:07, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 15:00 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Цитата:
Исходный пример должен бы работать, если бы не одна "мелочь".
При вызове ArrayIncrement Delphi передает максимальный индекс, а ББ ожидает длину массива.


Игорь, тогда я чего-то не догоняю: передавать-то передает, но в BB размер массива определяется по его тегу типа, и этот тег типа лежит в области памяти массива. А в Дельфе, кажется, информация об индексации открытого массива VAR array of... идет дополнительными параметрами в стеке. А у динамических массивов (без VAR) array of... размер лежит по смещению -4 от базового адреса, т.е. также совместимости с TAGGED ARRAY никакой нет...

Али я не прав?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 15:15 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1429
Нет. Размер массива передается как еще один параметр.
А без VAR это не динамический массив, это массив, для которого создается локальная копия в стеке.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 15:20 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Стоп, стоп...
PROCEDURE (a: array of integer) - это не статический открытый массив, таких в Дельфе просто нет.
array of... - это как раз динамический массив, память под который выделяется SetLength. Фактически, указатель на массив.

Я могу написать:
VAR a, b: array of integer
SetLength(a, 256); SetLength(b, 512);
a := b - тут я присвою указатели, и a и b станут указывать на один массив.

Посему
PROCEDURE (a: array of integer)
PROCEDURE (a: pIntArray)
и
PROCEDURE (VAR a: array [0..n] of char)
на низком уровне абсолютно идентичны, в заголовках к внешним DLL в Дельфе с одинаковым успехом используют все три формы.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 15:52 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1429
Илья Ермаков писал(а):
Стоп, стоп...
PROCEDURE (a: array of integer) - это не статический открытый массив, таких в Дельфе просто нет.

Ну, я не о Дельфе. :)
Впрочем, почему нет?
Код:
PROCEDURE p1(a: array of integer);
и
PROCEDURE p2(VAR a: array of integer);

отличаются только тем, что в p1 значение a ьбудет скопировано а стек.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 16:08 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1429
Trurl писал(а):
Исходный пример должен бы работать, если бы не одна "мелочь".

Вот, попробовал.
Код:
procedure ArrayIncrement(VAR x: array of integer); stdcall; external 'MyDll.dll';
var i: integer;
    a: array [0..4] of integer;
begin
  for i:= 0 to 4 do 
  begin
      a[i] := 0;
  end;
  ArrayIncrement(a);
  for i:= 0 to 4 do
  begin
      writeln(a[i]);
  end;
end.

выводит
1
1
1
1
0


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 17:03 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Да, Вы правы, в Дельфе это открытый массив, передаваемый по значению.
Вот строчка из документации:
The syntax of open array parameters resembles that of dynamic array types, but they do not mean the same thing.

Вот он, еще один пример путаницы в Object Pascal, когда разные вещи называются абсолютно одинаково.

Объявления
TYPE A: array of integer;
PROCEDURE P(a: A)
и
PROCEDURE p(a: array of integer) должны быть абсолютно идентичны, просто в одном случае тип объявлен явно, а в другом - по месту... На самом деле "две большие разницы" :-(


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 17:07 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Цитата:
Вот, попробовал.


Так что, форматы открытых массивов у Дельфи и ББ совпадают?
И все равно, мне кажется, для любых данных, приходящих извне, надо использовать [untagged], мало ли какие побочные эффекты могут быть...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 17:27 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 18:55
Сообщения: 2272
Откуда: Россия, Нижний Новгород
Илья Ермаков писал(а):
Так что, форматы открытых массивов у Дельфи и ББ совпадают?

...с точностью до разницы между High(a) и Length(a) :D
Trurl писал(а):
При вызове ArrayIncrement Delphi передает максимальный индекс, а ББ ожидает длину массива.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 22 Февраль, 2006 22:33 

Зарегистрирован: Вторник, 29 Ноябрь, 2005 21:41
Сообщения: 1030
Сергей Губанов писал(а):
Илья Ермаков писал(а):
Так что, форматы открытых массивов у Дельфи и ББ совпадают?

... до разницы между High(a) и Length(a) :D

Было бы интересно поглядеть где именно это описано. Если бы вопрос сводился к количеству обрабатываемых элементов то была бы непонятна причина возниковения ошибки в вышеуказанном примере. Кстати откуда известно, что она возникла в результате вызова ArrayIncrement(a)?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 23 Февраль, 2006 20:21 

Зарегистрирован: Среда, 22 Февраль, 2006 10:35
Сообщения: 144
Откуда: Новочеркасск
Цитата:
Кстати откуда известно, что она возникла в результате вызова ArrayIncrement(a)?

В Дельфе есть пошаговый отладчик...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 23 Февраль, 2006 21:52 

Зарегистрирован: Среда, 22 Февраль, 2006 10:35
Сообщения: 144
Откуда: Новочеркасск
Работает такой вариант:
Код:
MODULE MyDll;
(* sample module to be linked into a dll *)
IMPORT SYSTEM;
      
PROCEDURE ArrayIncrement*(VAR x: ARRAY [untagged] OF INTEGER; num: INTEGER);
VAR i: INTEGER;
BEGIN
 FOR i := 0 TO num - 1 DO
   INC(x[i]);
END;
END ArrayIncrement;
      
END MyDll.


Delphi:
Код:
PROCEDURE ArrayIncrement(x: pointer; num: integer); stdcall; external 'MyDll.dll';

procedure TForm1.Button2Click(Sender: TObject);
var i: integer;
    a: array [0..4] of integer;
begin
  for i:= 0 to 4 do
  begin
      a[i] := 0;
  end;
  ArrayIncrement(@a[0], 5);
end;

После ArrayIncrement(@a[0], 5) все 5 элементов массива a равны 1.
Информация к размышлению:
1) Любой из вариантов описания процедуры ArrayIncrement:
Код:
PROCEDURE ArrayIncrement(var x: array of integer; num: integer); stdcall; external 'MyDll.dll';

Код:
PROCEDURE ArrayIncrement(x: array of integer; num: integer); stdcall; external 'MyDll.dll';
не проходит. После вызова процедуры с любым num, последний элемент массива a всегда равен 0(?!).
2) Вызов ArrayIncrement(@a[0], 500) не производит никакой исключительной ситуации(!);


Цитата:
И все равно, мне кажется, для любых данных, приходящих извне, надо использовать [untagged], мало ли какие побочные эффекты могут быть...

Действительно, вчера исходный пример вызывал исключение, а сегодня, после перекомпиляции, выдавал то же самое, что и у Trurl:
Цитата:
выводит
1
1
1
1
0


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 23 Февраль, 2006 23:50 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Цитата:
После вызова процедуры с любым num, последний элемент массива a всегда равен 0(?!).


Видимо, как раз из-за дополнительного параметра-предела индексов, который Дельфа передает перед каждым открытым массивом. А [untagged]-открытый массив в ББ - это по сути просто указатель, без всякой скрытой информации.

Я Вас тоже подзапутал с этими открытыми массивами, выше я уже написал про свою ошибку с array of...

Самый нормальный вариант, как Вы и сделали - в ББ - untagged-массив, а в Дельфе - указатель. Можно указатель на массив, не обязательно именно pointer.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пятница, 24 Февраль, 2006 07:15 

Зарегистрирован: Вторник, 29 Ноябрь, 2005 21:41
Сообщения: 1030
Я конечно могу и ошибаться. По идее вариант
Код:
BlackBox:
PROCEDURE ArrayIncrement*(VAR x: ARRAY [untagged] OF INTEGER, index: INTEGER);

Delphi:
PROCEDURE ArrayIncrement(var x: array of integer); stdcall; external 'MyDll.dll';

должен работать. А если не работает надо узнать почему.
Что нам по этому поводу говорит пошаговый отладчик?


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 38 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 10


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2024, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB