Проверка указателя на объект

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Проверка указателя на объект

Сообщение gluhow » 03.11.2020 10:20:36

У меня немного странная задача: я отдельно сохраняю значение указателей памяти на некие объекты типа TTest. Нужно при получении указателя убедиться что он указывает на корректный объект моего типа и уже с ним работать. У меня сделано
Код: Выделить всё
Function GetTTestFromP(P:Pointer):TTest;
begin
try
  if TObject(P) is TTest then
    Result:=TTest(P)
  else
   Result:=nil;
except
  Result:=nil;
end;
end;

Проблема в том что указатель может оказаться совершенно левым. И на преобразовании TObject(P) выскакивает SIGSEGV. Возможно ли это как-то решить или всё-таки придется править логику?
gluhow
новенький
 
Сообщения: 41
Зарегистрирован: 13.08.2015 15:30:20

Re: Проверка указателя на объект

Сообщение zub » 03.11.2020 11:11:24

В общем случае - никак. Придется
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Проверка указателя на объект

Сообщение Alex2013 » 03.11.2020 13:45:46

Проверить TypeOf(P^) = TypeOf(TTest) ? :roll: (Неуверен в нужности " ^ " но идее должно работать )

{ Пример программы для функции TypeOf }

procedure GraphicalObject.Move;
begin
{ В зависимости от типа полученного значения, }
if TypeOf(Self) = TypeOf(ScrWindow) then
{ выполняем специальное действие, если перемещаем окно, или }
else
{ если нет, то другое действие }
end;
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Проверка указателя на объект

Сообщение gluhow » 03.11.2020 14:27:57

Alex2013 писал(а):Проверить TypeOf(P^) = TypeOf(TTest)
Так оно не компилится.
gluhow
новенький
 
Сообщения: 41
Зарегистрирован: 13.08.2015 15:30:20

Re: Проверка указателя на объект

Сообщение Alex2013 » 03.11.2020 15:50:59

gluhow писал(а):
Alex2013 писал(а):Проверить TypeOf(P^) = TypeOf(TTest)
Так оно не компилится.


Проверь TypeOf(P) без птички или объявили GetTTestFromP(P:^TObject ):TTest;

Добавлено спустя 9 минут 55 секунд:
Понял в чем дело !
Код: Выделить всё
procedure TForm1.Button1Click(Sender: TObject);
begin
   if TypeOf( Sender ) = TypeOf(Button1) then TButton(Sender).Caption:='OK';
end;


Все просто TypeOf работает не с типом, а только с экземпляром типа.
То есть TypeOf(ТButton) не компилируется TypeOf(Button1) все ок.

У тебя будет как то так .
Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;
  L1:Longint;
implementation

Function GetTTestFromP(P,R:Pointer):TForm;
begin
try
if  TypeOf( TObject(P^) ) = TypeOf(TObject(R^))  then
  Result:=TForm(P)
else
Result:=nil;
except
Result:=nil;
end;
end;

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);

begin

If GetTTestFromP(@L1,Form1)=Nil then Form1.Caption:='Nil';


end;

procedure TForm1.Button2Click(Sender: TObject);
begin
If GetTTestFromP(Form1,Form1)<>Nil then  Form1.Caption:='Ok Не Nil' ;

end;

end.


Зы
Извини поленился проверить сразу ...
Последний раз редактировалось Alex2013 03.11.2020 17:55:28, всего редактировалось 1 раз.
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Проверка указателя на объект

Сообщение zub » 03.11.2020 17:52:49

Alex2013
TypeOf - возвращает адрес VMT и появился для объектов, для них в качестве аргумента может быть как тип объекта, так и инстанс объекта
Код: Выделить всё
program Project1;

type
  ptobj=^tobj;
  tobj=object
    procedure a;virtual;abstract;
    constructor init;
  end;
  ptobj2=^tobj2;
  tobj2=object(tobj)
  end;

var
  pobj:ptobj;

constructor tobj.init;
begin
end;

begin
  pobj:=getmem(sizeof(tobj2));
  ptobj2(pobj)^.init;
  if typeof(pobj^)=typeof(tobj) then
    writeln('ptobj');
  if typeof(pobj^)=typeof(tobj2) then
    writeln('ptobj2');
  freemem(pobj);
  readln;
end.

Для классов в добавок к typeof появились еще оператор is и механизм rtti (также работает и для объектов)

Но к задаче тс это не имеет никакого отношения. проверить по адресу лежит там что-то валидное или нет - невозможно. разве что при создании данных в динамической памяти заносить их адреса в какойто список, при уничтожении этих данных удалять адреса из списка - проверка сведется к проверке наличия адреса в списке. но это только для данных которые ты создаешь сам
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Проверка указателя на объект

Сообщение Alex2013 » 03.11.2020 18:15:21

zub писал(а):Но к задаче тс это не имеет никакого отношения. проверить по адресу лежит там что-то валидное или нет - невозможно. разве что при создании данных в динамической памяти заносить их адреса в какойто список, при уничтожении этих данных удалять адреса из списка - проверка сведется к проверке наличия адреса в списке. но это только для данных которые ты создаешь сам

Пример что я сделал отличает Longint от TForm. Да, разумеется "дикая ссылка" может вести на "удаленный класс" но об этом должен позаботится "код контроля списка" не допуская там мусор . К тому-же в "общем виде" подобная проверка обычно не нужна . Чаще всего из потолка нужно выловить конкретный экземпляр класса или группу экземпляров . Но разумеется я соглашусь что в "общем виде " задача почти не решаема . :idea:
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Проверка указателя на объект

Сообщение zub » 03.11.2020 18:20:28

>>Пример что я сделал отличает и Longint от TForm
Longint от TForm невозможно как спутать, так и отличить. Ты очередную случайность пытаешься выдать за закономерность
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Проверка указателя на объект

Сообщение Alex2013 » 03.11.2020 18:29:01

zub писал(а):для них в качестве аргумента может быть как тип объекта, так и инстанс объекта

Почему же TypeOf(TButton) не компилируется ? " Error: class identifier expected"

Добавлено спустя 5 минут 15 секунд:
zub писал(а):>>Пример что я сделал отличает и Longint от TForm
Longint от TForm невозможно как спутать, так и отличить. Ты очередную случайность пытаешься выдать за закономерность

Вообще-то я пытаюсь определить TForm или НЕ TForm а это немного другое (Хотя признаюсь мучают меня "смутные сомнения" уж не сделали я просто извращенный вариант проверки адреса :wink: )
Зы
Когда мне было нужно обработать что-то "условно неизвестное" (даже "без индекса в списке", то есть бог знает в каком порядке его "владелец" выбирает данные из "внешнего списка" и вставляет в свой ) но порожденное от TControl я использовал "лишнее" поле HelpContext записывая туда при создании "внешний индекс" в результате "единый обработчик" знал что ему делать с конкретным экземпляром не имея никаких "внешних связей" .
В принципе похожим образом можно определять и все прочее затирая значение "в поле сигнатуры" при уничтожении класса .
Но это разумеется работает для "своих типов" где это поле точно есть .
Последний раз редактировалось Alex2013 03.11.2020 19:19:40, всего редактировалось 4 раз(а).
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Проверка указателя на объект

Сообщение zub » 03.11.2020 18:56:00

Потому что класс это не объект. и методы работы с классом "более продвинуты" - is и rtti
Вполне возможно от typeof вообще хотели отказаться как и от объектов. а вместо него
Код: Выделить всё
if Sender is TButton then ...

или ковыряние в rtti.
также от TButton наверно несложно получить его vmt или что там TypeOf возвращает для классов, но навскидку я не скажу как это сделать, надо проверять.

>>уж не сделали я просто извращенный вариант проверки адреса
нет, не сделал и не сделаешь.

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

Добавлено спустя 1 минуту 37 секунд:
вернее извращенный конечно сделаешь. 100% рабочий нет
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Проверка указателя на объект

Сообщение Alex2013 » 03.11.2020 19:10:17

zub писал(а):вернее извращенный конечно сделаешь. 100% рабочий нет

Уел ! :mrgreen:

Добавлено спустя 12 минут 25 секунд:
zub писал(а): тебе надо менять логику программы, чтобы подобных проблем не возникало

Задача не моя, я в этом топике как говорится "мимокрокодил". :idea:
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Проверка указателя на объект

Сообщение Seenkao » 03.11.2020 19:56:12

Поставь флаг на "свои" объекты (либо воспользуйся свободными в объекте) и проверяй. Будет стоять этот флаг, значит объект твой.
Seenkao
энтузиаст
 
Сообщения: 526
Зарегистрирован: 01.04.2020 03:37:12

Re: Проверка указателя на объект

Сообщение olegy123 » 03.11.2020 22:20:35

gluhow писал(а):Проблема в том что указатель может оказаться совершенно левым. И на преобразовании TObject(P) выскакивает SIGSEGV. Возможно ли это как-то решить или всё-таки придется править логику?

А как с этим справляются в многозадачном режиме? Когда нужно использовать объект который только был выделен в памяти но не про инициализирован? Где если прочитать его значения то они содержать мусор.
Кому верить?

Все давно уже решено и продумано. Не надо заниматься дроч. создавать на базе RTTI свою RTTI

Объект создается в одном потоке: если указатель на объект не нуль то объект явно создан, если он нуль то он не создан или был удален. Чтобы объект был явно занулен при удалении вместо Object.Free нужно вызывать FreeAndNil(Object) тогда указатель явно будет занулен, а объект разрушен.
Если вы выехали, прокатались, заехали в гараж и остановились - то никаких в жизни SIGSEGV не увидите.

В случае с многозадачностью - необходима блокировка(мьютекс, семафор,событие) чтобы понять в каком состоянии находится объект.

То есть в режиме одного потока - хватить проверка на nil, при многозадачном режиме - добавляются блокировки.

Добавлено спустя 8 часов 15 минут 1 секунду:
gluhow писал(а):У меня немного странная задача: я отдельно сохраняю значение указателей памяти на некие объекты типа TTest

если необходимо работать со сылками на объекты - то нужно обеспечить синхронизацию и работать с объектами через ссылки если ( ссылки => объект ), по ссылки переходим на объект разрушаем, ссылку зануляем и/или удаляем, если это список.
Если нет возможности напрямую воздействовать на объект, он создается и удаляется в другой системе, то должна быть обратная связь на изменения состояния( Callback, Events) - синхронизация в обратную сторону ( ссылки <= объект ), тогда по событии просто зануляем и/или удаляем ссылку, если это список. Как пример сокеты, файловые переменные и тому пободное, вообще работа с OS происходить именно так, вы просите создать объект операционной системе, OS вам дает либо ссылку на объект либо как правило nil. Вся работа происходить через эту ссылку: проверка состояний, события.
Те же окна Form - само окно реально создается в OS вам дается ссылка(Handle) на это окно.

Добавлено спустя 31 минуту 25 секунд:
gluhow писал(а):Проблема в том что указатель может оказаться совершенно левым.

Объекты обертки - вы создаете обертку со ссылкой на левый объект
Form <- это объект в вашей программе
+ Handle <- это некий указатель на объект в другой системе

сохраняете свою Form в списке и работаете с реальным объектом через Form.Handle
Код: Выделить всё
type
  TMyObject = class
    FHandle:Pointer
  end;

var
   MyObjectList:TList<TMyObject>;
Function GetTTestFromP(P:Pointer):TMyObject;
var
  myObj:TObject;
begin
  Result:=nil;
  for myObj in MyObjectList do
  begin
    if myObj.FHandle=P then begin
        Result:=myObj;
        break;
    end;
end;
end;
olegy123
долгожитель
 
Сообщения: 1643
Зарегистрирован: 25.02.2016 12:10:20

Re: Проверка указателя на объект

Сообщение zub » 05.11.2020 01:33:53

>>также от TButton наверно несложно получить его vmt или что там TypeOf возвращает для классов, но навскидку я не скажу как это сделать, надо проверять.
Код: Выделить всё
procedure TForm1.Button1Click(Sender: TObject);
begin
  if typeof(sender)=pointer(TButton.ClassType) then
   Button1.caption:='TButton';
end;

ClassType возвращает TClass, приходится приводить к указателю
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Проверка указателя на объект

Сообщение MylnikovDm » 20.11.2020 23:51:48

Объясняю популярно в чём состоит проблема.

При выделении памяти под экземпляр класса мы в переменную получаем указатель на начало данных класса. Но на самом деле указатель на виртуальную таблицу методов (VMT) тоже где-то должен присутствовать вместе с экземляром объекта. И этот указатель расположен ПЕРЕД данными объекта. Другими словами, объекту выделяется +4 в 32 и +8 в 64 системе байта, а указатель, который нам возвращает конструктор, и который мы сохраняем в переменной типа TObject указывает на выделенную область со смещением в эти самые 4 или 8 байт.

Если наш указатель указывает на экземпляр класса, то тогда перед ним всегда будет корректный указатель на VMT того или иного класса. Поэтому при попытке обратиться по этому адресу ошибки не будет. Но если у нас будет указатель не на объект, а на обычную скалярную переменную или запись, то никаких дополнительных байт с корректным указателем на VMT перед ней, естественно, не будет. Соответственно, попытка привести TObject(P) для последующей проверки класса будет вызывать ошибку доступа к памяти, если предыдущие 4 (8) байт указывают чёрти куда. Но нам может повезти, если они указывают в допустимую область памяти. В этом случае ошибки не будет. То есть, получаем классический вариант, когда в одних случаях программа работает, а в других не работает.

Из сказанного выше также следует простой вывод. Приводить к указатель к класса через TObject(P) можно только в том случае, если мы уверены, что P точно указывает на экземпляр класса, то есть, что у него точно правильно инициализирован указатель на VMT. Если это указатель, например, на переменную Longint, то мы можем получить ошибку, а можем и не получить, как повезёт.

По поводу работы функции TypeOf. В документации на FreePascal чётко сказано, что данная функция возвращает указатель на VMT экземпляра класса. То есть, она возвращает нам тот самый указатель, который записан перед объектом. Поэтому со скалярными переменными типа Longint она корректно работать не может в принципе. При этом код, который привёл в качестве примера Alex2013 на самом деле будет отличать longint от TForm, поскольку у переменной longint предыдущие 4(8) байт с очень высокой вероятностью не будут указывать на VMT класса TForm. И этот код не будет вызывать ошибки обращения к памяти, поскольку в данном случае сравнивается содержимое указателей на VMT, а не данные, которые записаны в VMT. Другими словами, программа не пытается обращаться по тем адресам, которые получила, а сравнивает сами значения адресов.

Но вот только я не уверен, что компилятор пропустит код TypeOf(P), если P будет объявлена как longint, поскольку там вроде как должен быть экземпляр класса. Хотя нетипизированный указатель P: Pointer может быть и пропустит (тут надо пробовать).

Ещё одна причина, когда может возникать ошибка обращения к памяти, это если переменная, на которую пришёл указатель, начинается с начала допустимого сегмента. То есть, недопустимо само обращение к тем четырём или восьми байтам, которые находятся перед ней. Ну и, само собой, если в указателе вообще пришёл какой-то мусор, который ведёт непонятно куда.

Но смешивание указателей на объекты и обычные переменные это очень плохая методика программирования. В хорошо спроектированной программе такого в принципе не должно быть. Мухи должны быть отдельно, а котлеты отдельно!!!
MylnikovDm
постоялец
 
Сообщения: 103
Зарегистрирован: 15.02.2007 21:26:10
Откуда: Челябинск

След.

Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8

Рейтинг@Mail.ru