Ответ 1
Обновлено. См. бит для решения generics.
Вот альтернатива, которая инкапсулирует тип, необходимый для смещения внутри функции, которая находится в расширенной записи, объявленной как функция класса. Помимо скрытия типа cast, смещение проверяется диапазоном на высокий индекс массива.
При необходимости могут быть добавлены дополнительные типы.
Type
SubRange = record
Type
TIntLongArray = array[0..MaxInt div SizeOf(Integer) - 1] of integer;
PIntLongArray = ^TIntLongArray;
TByteLongArray = array[0..MaxInt div SizeOf(Byte) - 1] of Byte;
PByteLongArray = ^TByteLongArray;
class function Offset( const anArray : array of Integer;
offset : Integer) : PIntLongArray; overload; static;
class function Offset( const anArray : array of Byte;
offset : Integer) : PByteLongArray; overload; static;
// ToDo: Add more types ...
end;
class function SubRange.Offset(const anArray : array of Integer;
offset : Integer): PIntLongArray;
begin
Assert(offset <= High(anArray));
Result := PIntLongArray(@anArray[offset]);
end;
class function SubRange.Offset(const anArray : array of Byte;
offset : Integer): PByteLongArray;
begin
Assert(offset <= High(anArray));
Result := PByteLongArray(@anArray[offset]);
end;
Примечание: сумма смещения и среза не должна превышать количество элементов массива.
Примеры вызовов:
WorkWithArray( Slice(SubRange.Offset(staticArray,1)^,2));
WorkWithArray( Slice(SubRange.Offset(dynArray,1)^,2));
WorkWithArray( Slice(SubRange.Offset(dynArrayG,1)^,2));
Пока это выглядит лучше, я все еще не убежден, что это оптимальное решение.
Обновление
При написании вышеупомянутого решения у меня в качестве конечной цели было решение generics.
Вот ответ, который использует анонимные методы и обобщения для реализации Slice(anArray,startIndex,Count)
метод, который может использоваться как с статическими, так и с динамическими массивами.
Решение с прямыми генериками будет полагаться на проверку диапазона, которая будет отключена на каждом месте, где она была использована,
и это не будет хорошим решением.
Причина в том, что SizeOf(T)
не может использоваться для объявления типа статического массива максимального размера:
TGenericArray = array [0..MaxInt div SizeOf (T) - 1] of T;//SizeOf (T) не разрешено
Поэтому нам нужно будет использовать:
TGenericArray = массив [0..0] of T;
вместо этого. И это вызывает проверку диапазона, когда он включен, для индексa > 0.
Решение
Но проблема может быть решена другой стратегией, callbacks
или более современной терминологией будет Inversion of Control
(IoC) или Dependeny Injection
(DI).
Концепцию лучше всего пояснить: "Не звоните мне, мы вам звоним".
Вместо использования прямой функции мы передаем операционный код как анонимный метод вместе со всеми параметрами.
Теперь проблема проверки диапазона содержится в кадре Slice<T>
.
Slice<Integer>.Execute(
procedure(const arr: array of Integer)
begin
WriteLn(Math.SumInt(arr));
end, dArr, 2, 7);
unit uGenericSlice;
interface
type
Slice<T> = record
private
type
PGenericArr = ^TGenericArr;
TGenericArr = array [0..0] of T;
public
type
TConstArrProc = reference to procedure(const anArr: array of T);
class procedure Execute( aProc: TConstArrProc;
const anArray: array of T;
startIndex,Count: Integer); static;
end;
implementation
class procedure Slice<T>.Execute(aProc: TConstArrProc;
const anArray: array of T; startIndex, Count: Integer);
begin
if (startIndex <= 0) then
aProc(Slice(anArray, Count))
else
begin
// The expression PGenericArr(@anArray[startIndex]) can trigger range check error
{$IFOPT R+}
{$DEFINE RestoreRangeCheck}
{$R-}
{$ENDIF}
Assert((startIndex <= High(anArray)) and (Count <= High(anArray)-startIndex+1),
'Range check error');
aProc(Slice(PGenericArr(@anArray[startIndex])^, Count));
{$IFDEF RestoreRangeCheck}
{$UNDEF RestoreRangeCheck}
{$R+}
{$ENDIF}
end;
end;
end.
Вот несколько примеров использования:
program ProjectGenericSlice;
{$APPTYPE CONSOLE}
uses
Math,
uGenericSlice in 'uGenericSlice.pas';
function Sum(const anArr: array of Integer): Integer;
var
i: Integer;
begin
Result := 0;
for i in anArr do
Result := Result + i;
end;
procedure SumTest(const arr: array of integer);
begin
WriteLn(Sum(arr));
end;
procedure TestAll;
var
aProc: Slice<Integer>.TConstArrProc;
dArr: TArray<Integer>;
mySum: Integer;
const
sArr: array [1 .. 10] of Integer = (
1,2,3,4,5,6,7,8,9,10);
begin
dArr := TArray<Integer>.Create(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
aProc :=
procedure(const arr: array of Integer)
begin
WriteLn(Sum(arr));
end;
// Test predefined anonymous method
Slice<Integer>.Execute( aProc, dArr, 2, 7);
// Test inlined anonymous method
Slice<Integer>.Execute(
procedure(const arr: array of Integer)
begin
WriteLn(Sum(arr));
end, dArr, 2, 7);
// Test call to Math.SumInt
Slice<Integer>.Execute(
procedure(const arr: array of Integer)
begin
WriteLn(Math.SumInt(arr));
end, dArr, 2, 7);
// Test static array with Low(sArr) > 0
Slice<Integer>.Execute(
procedure(const arr: array of Integer)
begin
WriteLn(Sum(arr));
end, sArr, 3 - Low(sArr), 7);
// Using a real procedure
Slice<Integer>.Execute(
SumTest, // Cannot be nested inside TestAll
dArr, 2, 7);
// Test call where result is passed to local var
Slice<Integer>.Execute(
procedure(const arr: array of Integer)
begin
mySum := Math.SumInt(arr);
end, dArr, 2, 7);
WriteLn(mySum);
end;
begin
TestAll;
ReadLn;
end.