
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.4] |
![]() |
|
Сообщ.
#1
,
|
|
|
Вот есть некая процедура и один из её параметров это свой тип например:
![]() ![]() type TA1 = packed array[0..15] of byte; и я хочу вызвать эту процедуру но уже с использованием другого типа который по сути тот же самый, и получаю ошибку - Invalid typecast пример: ![]() ![]() procedure proc(Value: Ta1); begin ........ end; ... var ab: Tbytes; aab: array of byte; begin proc(ab); - Invalid typecast proc(aab); - Invalid typecast end; Понятное дело, что можно свести всё к одному типу, но на этот перебор будет уходить время, переписывать процедуры тоже плохой вариант, поэтому вопрос может как-то можно сделать то что я хочу с минимальной затратой ресурсов ? |
Сообщ.
#2
,
|
|
|
переписать только функцию:
![]() ![]() procedure proc(const Value; const ASize: DWORD); var ptr: pbyte; begin ptr:= @Value; // далее - работа через ptr^ end; |
Сообщ.
#3
,
|
|
|
![]() ![]() type PA1 = ^TA1; TA1 = packed array[0..15] of byte; ... begin Proc(PA1(@ab[0])^); Proc(PA1(@aab[0])^); end; |
Сообщ.
#4
,
|
|
|
Параметр - открытый массив (Value: array of Byte) не подойдёт?
P.S. Пример не соответствует фразе >другого типа который по сути тот же самый |
Сообщ.
#5
,
|
|
|
Jiro
Один из вариантов - использовать в качестве параметра открытый массив Value:array of byte, вместо которого можно будет передавать и статические (типа ТА1), и динамические (типа TBytes) массивы байтов. Другой вариант - передавать массив по указателю на первый элемент ![]() ![]() type pA1 = ^TA1; TA1 = packed array[0..15] of byte; procedure proc(Value: pA1); ... proc(@ab[0]); //при таком вызове ab м.б. и статическим и динамическим |
Сообщ.
#6
,
|
|
|
Всем thx.
Хотелось именно без модификации процедуры, тк. процедур много и в каждой свой тип и они не мои, а из чужего юнита, который апдейтится не мной. |
![]() |
Сообщ.
#7
,
|
|
а зачем? в процедуру передается строгий тип на 16 байт по ссылке, ты же хочешь впихнуть туда динамический тип. Компилятор в данном случае уже контролировать не сможет. в нутри функции тоже отследить динамический массив не получится (так как функция думает что это статический массив). и это приведет только к одному, к AV.
но как вариант можно решить проблему за счет хелперов или операторов helper ![]() ![]() uses System.Math; type TBytesHelper = record helper for TBytes public function toTA1: TA1; end; { TBytesHelperHelper } function TBytesHelper.toTA1: TA1; begin FillChar(Result, sizeof(Result), 0); if Length(Self) > 0 then Move(Self[0], Result, Min(sizeof(Result), Length(Self))); end; //use proc(ab.toTA1); operator ![]() ![]() type TMyRecord = record private InternalArray: TBytes; public constructor Create(arr: TBytes); overload; constructor Create(Memory: PByte; Size: NativeInt); overload; class operator Implicit(Value: TMyRecord): TA1; end; { TMyRecord } constructor TMyRecord.Create(arr: TBytes); begin InternalArray := arr; end; constructor TMyRecord.Create(Memory: PByte; Size: NativeInt); begin SetLength(InternalArray, size); if Size > 0 then Move(Memory^, InternalArray[0], Size); end; class operator TMyRecord.Implicit(Value: TMyRecord): TA1; begin FillChar(Result, sizeof(Result), 0); if Length(Value.InternalArray) > 0 then Move(Value.InternalArray[0] ,Result, Min(sizeof(Result), Length(Value.InternalArray))); end; //use var Array1: TBytes; rc: TMyRecord; ... Array1 := TBytes.Create(1,2,3,4,5,6,7,8,9,0); rc := TMyRecord.Create(Array1); proc(rc); //или попроще proc(TMyRecord.Create(TBytes.Create(1,2,3,4,5,6,7,8,9,0))); |
Сообщ.
#8
,
|
|
|
Цитата ViktorXP @ FillChar(Result, sizeof(Result), 0); Хм, с каких пор Result стал получать осмысленное значение до вызова функции? Добавлено Цитата Jiro @ и я хочу вызвать эту процедуру но уже с использованием другого типа который по сути тот же самый, и получаю ошибку - Invalid typecast Не тот же самый. Динамический массив и фиксированный массив - два физически очень разных типа. Если не имеешь возможности переделать модуль - да, только пиши конверторы из своего типа в используемый. Либо добавляй обертки типа ![]() ![]() function proc_mytype(A: Tbytes) begin proc(TBytesToTA1(A)); end; |
Сообщ.
#9
,
|
|
|
Цитата Fr0sT @ Хм, с каких пор Result стал получать осмысленное значение до вызова функции? С незапамятных - для сложных типов Result передается как дополнительный var-параметр и соотв-но содержит то, что в нем было до вызова функции. PS: Особенно "прикольно\неожиданно" это выглядит в функциях, возвращающих string |
Сообщ.
#10
,
|
|
|
leo, забавно, спасибо. Хотя это скорее хакофича, и рассчитывать на нее не стоит, к тому же это повергает в когнитивный диссонанс читающего код
|
![]() |
Сообщ.
#11
,
|
|
Цитата Fr0sT @ Хотя это скорее хакофича, и рассчитывать на нее не стоит, к тому же это повергает в когнитивный диссонанс читающего код ???? Result является полноценной переменной (параметром) и всегда таким был Не путай с С/С++ в которой до return результата нет. То что ты не использовал эту возможность (хотя я уверен что использовал. просто сам этого не знал или не задумывался об этом) не коем образом не делает ее "хаком". Добавлено пс. в доказательства, что это полноценная переменная, можешь набросать примеры и увидеть что result подчиняется всем законам переменных. да и в стандарте можно по ковыряться паскалевском. я думаю там все это задокументировано |
![]() |
Сообщ.
#12
,
|
|
ViktorXP
Можно простой пример? Я хочу понять о чем речь. |
Сообщ.
#13
,
|
|
|
ViktorXP, покажи мне реально оправданный пример использования предыдущего значения Result.
|
![]() |
Сообщ.
#14
,
|
|
для начала (что бы я не быть голословным) загляним в Object Pascal Language Guide
там есть писание синтаксиса и поведения функции (нам интересны только аспекты поведения result) ![]() Тут сказано что в каждой функции есть неявная локальная переменная result, которая имеет тот же тип что и результат функции. и присвоение ей значение будет тоже самое что и присвоение наименованию функции. также локальную переменную result можно использовать для получения значения результата внутри функции. то есть функцию ![]() ![]() function MyFunc(): Integer; можно рассматривать как ![]() ![]() function MyFunc(): Integer; var Result: Integer absolute MyFunc;//так она не соберется но для наглядности сойдет если интересно также для наглядности мы можем рассмотреть эту функцию как процедуру с out параметром ![]() ![]() procedure MyFunc(out Result: Integer); Предположу что тебя смущает ошибка "W1035 Return value of function 'MyFunc' might be undefined" при попытке считать значение из result (или выходя из функции) не проинициализировав ее перед этим. но сама переменная от этого никуда не делась, под ее даже память выделена (изредка конечно нет, но в данной ситуации мы это не рассматриваем) в довесок сообщу что ты также получишь ошибку если попытаешься работать с не проинициализированой локальной переменной ![]() ![]() function MyFunc(): Integer; var I: Integer; begin if Result = 1 then // W1035 Return value of function 'MyFunc' might be undefined Exit(10); if I = 1 then // W1036 Variable 'I' might not have been initialized Exit(1); end; Это связано с тем что сам "компилятор" не чистит стек для локальных переменных и результата. и там может находится все что угодно (мусор от предыдущих функций/процедур) это не касается массивов/строк/интерфейсов, "компилятор" их проинициализирует (это оговорено в том же Guide. будет желание можешь поискать) ![]() ![]() function MyFunc(): string; begin if Result = '' then // никакой ошибки не будет Exit('empty'); end; В чем же есть оправдания использовать result сразу, без использования дополнительной переменной? а переодически вижу код ![]() ![]() function MyFunc(): Integer; var Ret: Integer; begin Ret := 0; ... Result := ret; end; и в первом, и во втором случае это говно-код. Бессмысленно заводить переменную если она у тебя уже есть. Если на простом типе integer ты теряешь всего 4 байта (x32). то в случае структуры в 16 байт ты в стек положешь лишние 16 байт. При этом еще и потеряешь на копировании при выходе из функции (ведь нужно скопировать твои данные из локальной переменной в result). ![]() ![]() type TMyChars = array[0..9] of char; function MyFunc(): TMyChars; var LocalParam: TMyChars; begin LocalParam := '0123456789'; Result := LocalParam; end; ![]() пс. я надеюсь что свою мысль я донес максимально доступно. пс.Imge извиняюсь если картинки получились через чур большими, у меня разрешение дисплея большое. (картинки выглядят маленькими) |
Сообщ.
#15
,
|
|
|
ViktorXP, спасибо и уважуха за развернутый ответ, но ты меня не понял. По большей части это все азбука и мне, разумеется, известно. Возражения у меня вызвала вот эта конструкция
![]() ![]() function TBytesHelper.toTA1: TA1; begin FillChar(Result, sizeof(Result), 0); if Length(Self) > 0 then Move(Self[0], Result, Min(sizeof(Result), Length(Self))); end; т.к. в переопределенные типы я не вникал, то решил, что Result - это динамический массив, длину которого ты с какого-то перепугу используешь. Сейчас посмотрел - TA1 - статический массив, для него все верно. Т.ч. вопрос исчерпан. Хотя вот leo понял причину моего недоумения ![]() |
Сообщ.
#16
,
|
|
|
Цитата ViktorXP @ Тут сказано что в каждой функции есть неявная локальная переменная result ... также получишь ошибку если попытаешься работать с не проинициализированой локальной переменной Это связано с тем что сам "компилятор" не чистит стек для локальных переменных и результата. ... это не касается массивов/строк/интерфейсов, "компилятор" их проинициализирует Не совсем так. Если заглянуть "под капот" Program Control -> Handling Function Results, то увидим, что результат функции "сложного" типа (к коим относятся статические массивы, записи и множества размером более 4 байт, а также managed типы строки\дин.массивы\варианты) передаются в функцию как дополнительный var-параметр. Поэтому в случае с возвратом строки компилятор не ругается на проверку Result не потому, что "локальная переменная" Result:string автоматически инициализируется нулем внутри функции, а потому, что в данном случае Result - это var-параметр, обязанность инициализации которого лежит на вызывающей стороне. Точно также компилятор не будет ругаться и в случае не managed-типа результата - записи, стат.массива или множества. Например: ![]() ![]() function Foo1:string; //по сути это procedure _Foo1(var Result:string); begin if Result = '' then Result:='empty string'; ShowMessage(Result); end; function Foo2:TPoint; //по сути это procedure _Foo2(var Result:TPoint); begin inc(Result.X); inc(Result.Y); ShowMessage(Format('%d; %d',[Result.X,Result.Y])); end; var s:string; P:TPoint; begin s:='Hello,World'; // s:=Foo1; //= _Foo1(s); P.X:=10; P.Y:=10; P:=Foo2; //= _Foo2(P); end; поскольку тут Foo1 и Foo2 по сути являются процедурами с var-параметрами, то компилятор НЕ ругается не только на отсутствие инициализации Result внутри этих функций, но также и на якобы неиспользуемые присваивания значений переменных s и P в вызывающей функции. Добавлено PS: Поэтому в общем случае, Result это не такая уж "обычная локальная переменная", как следует из ее "высокоуровневого" описания. В частности, присваивание Result "сразу, без использования дополнительной переменной" может приводить к различному поведению\результату в случае необработанного исключения в функции - для типов Result, возвращаемых через регистры, значение переменной, которой присваивается значение функции, в случае ошибки не изменится, а для типов, передаваемых через var-параметр оно меняется "сразу" и соотв-но будет содержать значение Result на момент возникновения исключения. |
![]() |
Сообщ.
#17
,
|
|
Это уже особенности реализации компилятора. Которые не опровергают существования result как локальной переменной, но накладывает небольшие условия.
пс. но странно будет сотрется код в котором при исключении я не буду корректировать возвращаемый результат или иными способами обрабатывать это исключение. |