#arrays #delphi #dynamic #initialization #record
#массивы #delphi #динамический #инициализация #запись
Вопрос:
Я пытаюсь выяснить, возможно ли инициализировать запись, содержащую динамический массив, используя «неявный» оператор класса в Delphi (Берлин 10.1 upd 1)
Прилагаемая программа выдает следующий вывод:
ci iA r1 r2 r3
1 1 1 1 49694764491115752
2 2 2 2 11570520
3 3 3 3 0
4 4 4 4 0
5 5 5 5 0
- TRec — это тип записи, содержащий динамический массив, который я хочу инициализировать.
- ci — это постоянный массив целых чисел.
- ia — это динамический массив целых чисел.
- r1, r2, r3 — это записи типа TRec, которые инициализируются по-разному.
Как вы можете видеть из выходных данных, первые два назначения (r1, r2) с использованием констант работают должным образом. Третье присвоение r3 := iArray
принимается компилятором, но результат нарушается. Отладчик показывает, что значение v
in TRec.Implicit
уже неверно.
Что здесь происходит не так? Возможно ли это вообще?
program Project5;
{$APPTYPE CONSOLE}
{$R *.res}
type
TRec = record
iArray: array of UInt64;
class operator Implicit(const v: array of UInt64): TRec;
end;
{ TRec }
class operator TRec.Implicit(const v: array of UInt64): TRec;
var
i: integer;
begin
setlength(Result.iArray, Length(v));
for i := 0 to High(v) do
Result.iArray[i] := v[i];
end;
const
ciArray: array [0 .. 4] of UInt64 = (1, 2, 3, 4, 5);
var
i : integer;
iArray : array of UInt64;
r1, r2, r3: TRec;
begin
iArray := [1, 2, 3, 4, 5];
r1 := [1, 2, 3, 4, 5];
r2 := ciArray;
r3 := iArray;
Writeln('ci iA r1 r1 r3');
for I := 0 to High(ciArray) do
Writeln(ciArray[i], ' ', iArray[i], ' ', r1.iArray[i], ' ', r2.iArray[i], ' ', r3.iArray[i]);
readln;
end.
Комментарии:
1. Конечно, это возможно. Я делаю это для своего
BigInteger
типа ( rvelthuis.de/programs/bigintegers.html ) тоже. Но убедитесь, что вы выполняете какое-то копирование при записи (COW), поэтому при каждом изменении массива вы должны убедиться, что это уникальная копия.2. @RudyVelthuis Ошибка компилятора делает это невозможным, не так ли. Если вы не исправите эту ошибку или не получите исправление от поставщика.
3. Ну, похоже, что параметры dynarray to
Implicit()
являются частным случаем. В противном случае это работает. Запись может содержать dynarray в порядке.4. Что ж, параметры массива dyn являются предметом обсуждения, поэтому давайте сосредоточимся на этом
5. Предметом вопроса являются «записи с помощью dynarray и и неявного оператора», а не «записи с неявным оператором с параметром открытого массива». Не то же самое. И это, похоже, исправлено в Delphi 10.2 Tokyo.
Ответ №1:
Похоже, вы обнаружили там ошибку с codegen (и она также существует в компиляторе Win64). Я просмотрел сгенерированный asm, и, похоже, вместо этого компилятор выдает неправильную инструкцию для перегрузки оператора. Вот почему неправильные значения оказываются в массиве внутри перегрузки оператора. Пожалуйста, сообщите об этом на портале качества.
Код, сгенерированный для плохого результата:
Project109.dpr.46: r3 := iArray;
0040B1F2 A1FC044100 mov eax,[$004104fc]
0040B1F7 8945E8 mov [ebp-$18],eax
0040B1FA 837DE800 cmp dword ptr [ebp-$18],$00
0040B1FE 740B jz $0040b20b
0040B200 8B45E8 mov eax,[ebp-$18]
0040B203 83E804 sub eax,$04
0040B206 8B00 mov eax,[eax]
0040B208 8945E8 mov [ebp-$18],eax
0040B20B 8D4DD8 lea ecx,[ebp-$28]
0040B20E 8B55E8 mov edx,[ebp-$18]
0040B211 4A dec edx
0040B212 B8FC044100 mov eax,$004104fc // <-- wrong one
0040B217 E87CF5FFFF call TRec.amp;op_Implicit
Код для метода equal:
Project109.dpr.47: r3 := TRec.Implicit(iArray);
0040B22F A1FC044100 mov eax,[$004104fc]
0040B234 8945E4 mov [ebp-$1c],eax
0040B237 837DE400 cmp dword ptr [ebp-$1c],$00
0040B23B 740B jz $0040b248
0040B23D 8B45E4 mov eax,[ebp-$1c]
0040B240 83E804 sub eax,$04
0040B243 8B00 mov eax,[eax]
0040B245 8945E4 mov [ebp-$1c],eax
0040B248 8D4DD4 lea ecx,[ebp-$2c]
0040B24B 8B55E4 mov edx,[ebp-$1c]
0040B24E 4A dec edx
0040B24F A1FC044100 mov eax,[$004104fc] // <-- correct one
0040B254 E8CFF5FFFF call TRec.Implicit
Однако вы можете избежать этого, добавив еще одну перегрузку для неявного оператора с типом параметра TArray<UInt64>
, а затем также объявить вашу локальную переменную как этот тип, чтобы компилятор выбрал правильную перегрузку (ту, для которой он не генерирует неправильный код в этом случае).
Но имейте в виду, что это будет работать только тогда, когда вы передаете переменные типа TArray<UInt64>
и вызываете неправильную, когда у вас есть какая-либо другая динамика array of UInt64
из-за строгих правил типов Delphis.
Обновление: об этом дефекте сообщалось в RSP-16084 и исправлено в Delphi 10.2 Tokyo.