Как вызвать корову для строки, когда она не срабатывает автоматически


У меня есть запись, смотрите этот вопрос для получения справочной информации.

TDigits = AnsiString;  //Should be `= array of NativeUInt`, but string has COW

TBigint = record
  Digit: TDigits; // Unsigned number, LSB stored in D[0], MSB in D[size-1]
  Size: Byte; // Mininum = 4, maximum 127.
  MSI: Byte; // Most significant (native)integer minimum=1, maximum=127
  Sign: Shortint;
  class operator Implicit(a: Integer): TBigint;

Фон
Я использую класс bignum, который (почти) работает как обычные целые числа.
Так что a:= 1000000*10000000*12000000*10000000*1000000; дает совершенно полезные результаты. Для этого я использую запись с class operators. Они запускают автоматическое преобразование и инициализацию типов.
за исключением , когда нет преобразования, потому что я назначаю TBigint другому TBigint.

Решение
Воспользуйся Ansistring для хранения основных данных, он имеет copy-on-write и будет клонировать себя, когда это необходимо.

Проблема: (COW не работает, если Delphi не знает, что вы изменяете строку)
У меня есть несколько чистых ассемблерных подпрограмм, которые манипулируют цифрой dynamic array, замаскированной под Ansistring.

Однако, когда я делаю что-то вроде этого:

Label1.Caption:= BigintToStr(b);
..... this fires:

function BigintToStr(const X: TBigint): AnsiString;
var
  ..
  LocX:= x;   <<-- assignment, locX and X are joined at the hip.
  repeat
    D := DivBigint(LocX, 1000000000, LocX); <<-- this routine changes LocX
                                 ^^+-- but assembler routines bypass COW

X и LocX являются суставами в тазобедренном суставе, все, что происходит с одним, происходит с другим.
Ясно Дельфи не знает, что процедура asm DivBigint меняется LocX и поэтому корова в порядке.

Обходной путь
Если я изменю процедуру на:

function BigintToStr(const X: TBigint): AnsiString;
var
  ..
  LocX:= x;
  LocX.Digit[2]:= #0;  <<-- inconsequential change to force COW.
  repeat
    D := DivBigint(LocX, 1000000000, LocX);

Delphi получает все подсказки и работает просто отлично. LocX и X не связаны, и все работает нормально.
Однако я не хочу делать глупые изменения посреди какого-то пустого пространства.

Есть ли достойный / правильный / официальный * способ заставить вызвать корову в строках?
Что-то например, системный вызов?

*обведите ваш любимый вариант (с нарисованным от руки кругом)

2 3

2 ответа:

Должен быть комментарий, но нужно больше места...

Если вам нужно позвонить UniqueString или эквивалентно.
С таким же успехом можно сохранить динамическую запись.

Цитата из руководства:

После вызова SetLength S гарантированно ссылается на уникальную строку или массив - то есть на строку или массив с числом ссылок, равным единице. Если недостаточно памяти для перераспределения переменной, SetLength вызывает исключение EOutOfMemory.

Обратите внимание, что это поведение применимо даже при вызове SetLength(Length(myArray));.
Дельфи сделает для вас копию и распутает проблему.

Таким образом, оказывается, что нет никакой необходимости в усложнении с AnsiStrings в конце концов, пока вы вызываете SetLength в каждом методе, который принимает запись в качестве параметра var.

Преимущества
Это имеет дополнительное преимущество, что если вы вызовете SetLength для расширения массива (как это часто бывает), добавленное пространство будет инициализировано нулем. Что-то делает, не делает происходить с AnsiString.

Кроме того, нет необходимости беспокоиться о переводе размера, потому что ваш array of TXYZ уже знает размер своих элементов. При использовании AnsiString вам нужно добавить * SizeOf(somestruct) везде.

Не требуется никакой типизации, упрощающей код; и в отладчике данные отображаются так, как они спроектированы.

Простой вызов SetLength делает динамический массив коровьим

Как вы можете видеть, эти два экземпляра больше не связаны.