Как вызвать корову для строки, когда она не срабатывает автоматически
У меня есть запись, смотрите этот вопрос для получения справочной информации.
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 ответа:
Каждый метод, который изменяет буфер, должен перед выполнением модификации вызвать
UniqueString
.Фактически, эта деталь была предоставлена комментарием Крэйга Янгак моему ответу на ваш предыдущий вопрос.Гарантирует, что данная строка имеет число ссылок равное единице.
Если вы собираетесь сделать это жизнеспособным, вам нужно будет скрыть буфер. Сделайте его строго частным. Это означает, что вы можете получить к нему доступ только из методов вашей записи. И таким образом, вы можете быть уверены, что все, что изменяет буфер, вызовет
Лично я думаю, что лучшим решением было бы сделать тип неизменяемым.UniqueString
.
Должен быть комментарий, но нужно больше места...
Если вам нужно позвонить
UniqueString
или эквивалентно.
С таким же успехом можно сохранить динамическую запись.Цитата из руководства:
После вызова SetLength S гарантированно ссылается на уникальную строку или массив - то есть на строку или массив с числом ссылок, равным единице. Если недостаточно памяти для перераспределения переменной, SetLength вызывает исключение EOutOfMemory.
Обратите внимание, что это поведение применимо даже при вызове
Таким образом, оказывается, что нет никакой необходимости в усложнении с AnsiStrings в конце концов, пока вы вызываете SetLength в каждом методе, который принимает запись в качестве параметраSetLength(Length(myArray));
.
Дельфи сделает для вас копию и распутает проблему.var
.Преимущества
Кроме того, нет необходимости беспокоиться о переводе размера, потому что ваш
Это имеет дополнительное преимущество, что если вы вызоветеSetLength
для расширения массива (как это часто бывает), добавленное пространство будет инициализировано нулем. Что-то делает, не делает происходить с AnsiString.array of TXYZ
уже знает размер своих элементов. При использовании AnsiString вам нужно добавить* SizeOf(somestruct)
везде.Не требуется никакой типизации, упрощающей код; и в отладчике данные отображаются так, как они спроектированы.
Как вы можете видеть, эти два экземпляра больше не связаны.